Skip to content

Commit

Permalink
Add quick reference help modal + add responsive toggles
Browse files Browse the repository at this point in the history
  • Loading branch information
watsonbox committed Aug 22, 2024
1 parent 9b0f396 commit e525f7d
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 306 deletions.
33 changes: 18 additions & 15 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Export Spotify playlists using the Web API" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<title>Exportify</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

<div class="ribbon">
<a href="https://github.com/watsonbox/exportify">Fork me on Github</a>
</div>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Export Spotify playlists using the Web API" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<title>Exportify</title>
</head>

<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

<div class="ribbon d-none d-sm-table-cell">
<a href="https://github.com/watsonbox/exportify">Fork me on Github</a>
</div>

<div id="root"></div>
</body>

<div id="root"></div>
</body>
</html>
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function App() {
return (
<div className="App container">
<header className="App-header">
<div className="d-sm-none d-block mb-4" />
<TopMenu loggedIn={key.has('access_token')} />
<h1>
<FontAwesomeIcon icon={['fab', 'spotify']} color="#84BD00" size="sm" /> <a href={process.env.PUBLIC_URL}>Exportify</a>
Expand Down
14 changes: 7 additions & 7 deletions src/components/PlaylistRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ class PlaylistRow extends React.Component {
let playlist = this.props.playlist
const icon = ['fas', (this.state.exporting ? 'sync' : 'download')]

if(playlist.uri==null) return (
if (playlist.uri == null) return (
<tr key={playlist.name}>
<td>{this.renderIcon(playlist)}</td>
<td>{playlist.name}</td>
<td colSpan="2">This playlist is not supported</td>
<td>{this.renderTickCross(playlist.public)}</td>
<td>{this.renderTickCross(playlist.collaborative)}</td>
<td className="d-none d-sm-table-cell" colSpan="2">This playlist is not supported</td>
<td className="d-none d-sm-table-cell">{this.renderTickCross(playlist.public)}</td>
<td className="d-none d-md-table-cell">{this.renderTickCross(playlist.collaborative)}</td>
<td>&nbsp;</td>
</tr>
);
Expand All @@ -61,9 +61,9 @@ class PlaylistRow extends React.Component {
<td>{this.renderIcon(playlist)}</td>
<td><a href={playlist.uri}>{playlist.name}</a></td>
<td><a href={playlist.owner.uri}>{playlist.owner.display_name}</a></td>
<td>{playlist.tracks.total}</td>
<td>{this.renderTickCross(playlist.public)}</td>
<td>{this.renderTickCross(playlist.collaborative)}</td>
<td className="d-none d-sm-table-cell">{playlist.tracks.total}</td>
<td className="d-none d-sm-table-cell">{this.renderTickCross(playlist.public)}</td>
<td className="d-none d-md-table-cell">{this.renderTickCross(playlist.collaborative)}</td>
<td className="text-end">
<Button type="submit" variant="primary" size="xs" onClick={this.exportPlaylist} disabled={this.state.exporting} className="text-nowrap">
<FontAwesomeIcon icon={icon} size="sm" spin={this.state.exporting} /> Export
Expand Down
68 changes: 35 additions & 33 deletions src/components/PlaylistTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class PlaylistTable extends React.Component {
this.setSubtitle(`${min}-${max} of ${this.state.playlistCount} playlists for ${this.userId}`)
}
)
} catch(error) {
} catch (error) {
apiCallErrorHandler(error)
}
}
Expand Down Expand Up @@ -156,7 +156,7 @@ class PlaylistTable extends React.Component {
{ currentPage: page },
this.loadCurrentPlaylistPage
)
} catch(error) {
} catch (error) {
apiCallErrorHandler(error)
}
}
Expand All @@ -183,7 +183,7 @@ class PlaylistTable extends React.Component {
)

await this.loadCurrentPlaylistPage()
} catch(error) {
} catch (error) {
apiCallErrorHandler(error)
}
}
Expand All @@ -200,42 +200,44 @@ class PlaylistTable extends React.Component {
<ConfigDropdown onConfigChanged={this.handleConfigChanged} ref={this.configDropdown} />
{this.state.progressBar.show && progressBar}
</div>
<table className="table table-hover table-sm">
<thead>
<tr>
<th style={{width: "30px"}}></th>
<th>Name</th>
<th style={{width: "150px"}}>Owner</th>
<th style={{width: "100px"}}>Tracks</th>
<th style={{width: "120px"}}>Public?</th>
<th style={{width: "120px"}}>Collaborative?</th>
<th style={{ width: "100px" }} className="text-end">
<PlaylistsExporter
<div className="table-responsive-sm">
<table className="table table-hover table-sm">
<thead>
<tr>
<th style={{ width: "30px" }}></th>
<th>Name</th>
<th style={{ width: "150px" }}>Owner</th>
<th style={{ width: "100px" }} className="d-none d-sm-table-cell">Tracks</th>
<th style={{ width: "120px" }} className="d-none d-sm-table-cell">Public?</th>
<th style={{ width: "120px" }} className="d-none d-md-table-cell">Collaborative?</th>
<th style={{ width: "100px" }} className="text-end">
<PlaylistsExporter
accessToken={this.props.accessToken}
onPlaylistsExportDone={this.handlePlaylistsExportDone}
onPlaylistExportStarted={this.handlePlaylistExportStarted}
playlistsData={this.playlistsData}
searchQuery={this.state.searchQuery}
config={this.state.config}
/>
</th>
</tr>
</thead>
<tbody>
{this.state.playlists.map((playlist, i) => {
return <PlaylistRow
playlist={playlist}
key={playlist.id}
accessToken={this.props.accessToken}
onPlaylistsExportDone={this.handlePlaylistsExportDone}
onPlaylistExportStarted={this.handlePlaylistExportStarted}
playlistsData={this.playlistsData}
searchQuery={this.state.searchQuery}
config={this.state.config}
/>
</th>
</tr>
</thead>
<tbody>
{this.state.playlists.map((playlist, i) => {
return <PlaylistRow
playlist={playlist}
key={playlist.id}
accessToken={this.props.accessToken}
config={this.state.config}
/>
})}
</tbody>
</table>
})}
</tbody>
</table>
</div>
<div id="playlistsFooter">
<Paginator currentPage={this.state.currentPage} pageLimit={this.state.searchQuery === "" ? this.PAGE_SIZE : this.state.playlistCount} totalRecords={this.state.playlistCount} onPageChanged={this.handlePageChanged} />
</div>
</div>
</div >
);
} else {
return <div className="spinner" data-testid="playlistTableSpinner"></div>
Expand Down
64 changes: 63 additions & 1 deletion src/components/TopMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import React from "react"
import { Button } from "react-bootstrap"
import { Button, Modal, Table } from "react-bootstrap"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"

type TopMenuProps = {
loggedIn: boolean
}

class TopMenu extends React.Component<TopMenuProps> {
state = {
showHelp: false
}

handleToggleHelp = () => {
this.setState({ showHelp: !this.state.showHelp })
}

handleLogoutClick = () => {
window.location.href = `${window.location.href.split('#')[0]}?change_user=true`
}
Expand Down Expand Up @@ -37,12 +45,66 @@ class TopMenu extends React.Component<TopMenuProps> {
}

render() {
const helpButton = this.props.loggedIn ? (
<>
<Button id="infoButton" type="submit" variant="link" size="lg" onClick={this.handleToggleHelp} title="Help">
<FontAwesomeIcon icon={['fas', 'circle-info']} size="lg" />
</Button>
<Modal size="lg" show={this.state.showHelp} onHide={this.handleToggleHelp}>
<Modal.Header closeButton>

<Modal.Title>Quick Reference</Modal.Title>
</Modal.Header>
<Modal.Body>
<h5><FontAwesomeIcon icon={['fas', 'search']} size="sm" className="opacity-25 me-2" />Advanced Search Syntax</h5>
<Table size="sm" striped bordered>
<thead>
<tr>
<th>Search query</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public:true</code></td>
<td>Only show public playlists</td>
</tr>
<tr>
<td><code>public:false</code></td>
<td>Only show private playlists</td>
</tr>
<tr>
<td><code>collaborative:true</code></td>
<td>Only show collaborative playlists</td>
</tr>
<tr>
<td><code>collaborative:false</code></td>
<td>Don't show collaborative playlists</td>
</tr>
<tr>
<td><code>owner:me</code></td>
<td>Only show playlists I own</td>
</tr>
<tr>
<td><code>owner:[owner]</code></td>
<td>Only show playlists owned by <code>[owner]</code></td>
</tr>
</tbody>
</Table>

<p>For more detail please refer to the <a href="https://github.com/watsonbox/exportify" target="_blank" rel="noreferrer">full project documentation</a>.</p>
</Modal.Body>
</Modal>
</>
) : ''

const logoutButton = this.props.loggedIn ? <Button id="logoutButton" type="submit" variant="link" size="lg" onClick={this.handleLogoutClick} title="Change user">
<FontAwesomeIcon icon={['fas', 'sign-out-alt']} size="lg" />
</Button> : ''

return (
<div id="topMenu">
{helpButton}
<Button id="darkModeButton" type="submit" variant="link" size="lg" onClick={this.handleDarkModeClick} title="Toggle dark mode">
<FontAwesomeIcon icon={['fas', 'lightbulb']} size="lg" />
</Button>
Expand Down
Loading

0 comments on commit e525f7d

Please sign in to comment.