- Only allow ItemType:Audio from Jellyfin
- Fix NavBar for Mobile (Ugly hack but.. TO REWORK)
- Fixed registration page issues
- Add functionality to pull recent scrobbles to Dashboard
- Add MX record lookup validation for emails
- Add username validation for a-Z 0-9 _ and .
- Dashboard shows basic table of last 500 scrobbles.
This commit is contained in:
Daniel Mason 2021-03-30 21:36:28 +13:00
parent 7ae9a0cd66
commit 2f8aa2e502
Signed by: idanoo
GPG key ID: 387387CDBC02F132
31 changed files with 425 additions and 171 deletions

View file

@ -1,6 +1,7 @@
.homeBanner {
margin-top: 30px;
width: 100%;
max-width: 1100px;
}
.homeBannerItem {

View file

@ -39,22 +39,22 @@ class HomeBanner extends React.Component {
<div className="homeBanner">
<div className="homeBannerItem">
{this.state.isLoading
? <ClipLoader color="#6AD7E5" size="36" />
? <ClipLoader color="#6AD7E5" size={36} />
: <span className="homeBannerItemCount">{this.state.scrobbleCount}</span>}<br/>Scrobbles
</div>
<div className="homeBannerItem">
{this.state.isLoading
? <ClipLoader color="#6AD7E5" size="36" />
? <ClipLoader color="#6AD7E5" size={36} />
: <span className="homeBannerItemCount">{this.state.userCount}</span>}<br/>Users
</div>
<div className="homeBannerItem">
{this.state.isLoading
? <ClipLoader color="#6AD7E5" size="36" />
? <ClipLoader color="#6AD7E5" size={36} />
: <span className="homeBannerItemCount">{this.state.trackCount}</span>}<br/>Tracks
</div>
<div className="homeBannerItem">
{this.state.isLoading
? <ClipLoader color="#6AD7E5" size="36" />
? <ClipLoader color="#6AD7E5" size={36} />
: <span className="homeBannerItemCount">{this.state.artistCount}</span>}<br/>Artists
</div>
</div>

View file

@ -3,19 +3,33 @@
color: #CCCCCC;
}
.navLinkMobile {
color: #CCCCCC;
}
.navLink:hover {
color: #666666;
text-decoration: none;
}
.navLinkMobile:hover {
color: #666666;
text-decoration: none;
}
.navLinkLogin {
margin-left: 15px;
padding-left: 15px;
border-left: 1px solid #282c34;
}
.navLinkLoginMobile {
margin: 0 auto 0 auto;
padding: 10px;
text-align: center;
}
.nav-logo {
height: 50px;
margin: -15px 5px -15px -5px;
}

View file

@ -1,5 +1,5 @@
import { React, Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { Navbar, NavbarBrand, Collapse, Nav, NavbarToggler, NavItem } from 'reactstrap';
import { Link } from 'react-router-dom';
import logo from '../logo.png';
import './Navigation.css';
@ -21,12 +21,19 @@ const loggedInMenuItems = [
'Dashboard',
'About',
]
const isMobile = () => {
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
};
class Navigation extends Component {
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
// Yeah I know you might not hit home.. but I can't get the
// path based finder thing working on initial load :sweatsmile:
this.state = { active: "Home" };
this.state = { active: "Home", collapsed: true};
}
componentDidMount() {
@ -51,84 +58,150 @@ class Navigation extends Component {
eventBus.remove(LOGOUT);
}
_handleClick(menuItem) {
this.setState({ active: menuItem });
this.setState({ active: menuItem, collapsed: !this.state.collapsed });
}
toggleNavbar() {
this.setState({ collapsed: !this.state.collapsed });
}
// This is a real mess. TO CLEAN UP.
render() {
const activeStyle = { color: '#FFFFFF' };
const renderAuthButtons = () => {
if (this.state.isLoggedIn) {
return <div className="navLinkLogin">
const renderMobileNav = () => {
return <Navbar color="dark" dark fixed="top">
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
<Collapse isOpen={!this.state.collapsed} navbar>
{this.state.isLoggedIn ?
<Nav className="navLinkLoginMobile" navbar>
{loggedInMenuItems.map(menuItem =>
<NavItem>
<Link
to="/profile"
style={this.state.active === "profile" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "profile")}
className="navLink"
>Profile</Link>
<Link to="/" className="navLink" onClick={logout()}>Logout</Link>
</div>;
} else {
return <div className="navLinkLogin">
<Link
to="/login"
style={this.state.active === "login" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "login")}
className="navLink"
>Login</Link>
<Link
to="/register"
className="navLink"
style={this.state.active === "register" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "register")}
history={this.props.history}
>Register</Link>
</div>;
}
key={menuItem}
className="navLinkMobile"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem}
>{menuItem}</Link>
</NavItem>
)}
<Link
to="/profile"
style={this.state.active === "profile" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "profile")}
className="navLinkMobile"
>Profile</Link>
<Link to="/" className="navLink" onClick={logout()}>Logout</Link>
</Nav>
: <Nav className="navLinkLoginMobile" navbar>
{menuItems.map(menuItem =>
<NavItem>
<Link
key={menuItem}
className="navLinkMobile"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
</NavItem>
)}
<NavItem>
<Link
to="/login"
style={this.state.active === "login" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "login")}
className="navLinkMobile"
>Login</Link>
</NavItem>
<NavItem>
<Link
to="/register"
className="navLinkMobile"
style={this.state.active === "register" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "register")}
history={this.props.history}
>Register</Link>
</NavItem>
</Nav>
}
</Collapse>
</Navbar>
}
const renderMenuButtons = () => {
if (this.state.isLoggedIn) {
return <div>
{loggedInMenuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem}
>
{menuItem}
</Link>
)}
</div>;
} else {
return <div>
{menuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
)}
</div>;
}
const renderDesktopNav = () => {
return <Navbar color="dark" dark fixed="top">
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
{this.state.isLoggedIn ?
<div>
{loggedInMenuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem}
>
{menuItem}
</Link>
)}
</div>
: <div>
{menuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
)}
</div>
}
{this.state.isLoggedIn ?
<div className="navLinkLogin">
<Link
to="/profile"
style={this.state.active === "profile" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "profile")}
className="navLink"
>Profile</Link>
<Link to="/" className="navLink" onClick={logout()}>Logout</Link>
</div>
:
<div className="navLinkLogin">
<Link
to="/login"
style={this.state.active === "login" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "login")}
className="navLink"
>Login</Link>
<Link
to="/register"
className="navLink"
style={this.state.active === "register" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "register")}
history={this.props.history}
>Register</Link>
</div>
}
</Navbar>
}
return (
<div>
<Navbar color="dark" dark fixed="top">
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
{renderMenuButtons()}
{renderAuthButtons()}
</Navbar>
{
isMobile()
? renderMobileNav()
: renderDesktopNav()
}
</div>
);
}

View file

@ -0,0 +1,43 @@
import React from "react";
class ScrobbleTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
};
}
render() {
return (
<div>
<table border={1} cellPadding={5}>
<thead>
<tr>
<td>Timestamp</td>
<td>Track</td>
<td>Artist</td>
<td>Album</td>
</tr>
</thead>
<tbody>
{
this.state.data && this.state.data.items &&
this.state.data.items.map(function (element) {
return <tr>
<td>{element.time}</td>
<td>{element.track}</td>
<td>{element.artist}</td>
<td>{element.album}</td>
</tr>;
})
}
</tbody>
</table>
</div>
);
}
}
export default ScrobbleTable;