- Switch redux -> Context
- Remove excess packages
This commit is contained in:
Daniel Mason 2021-03-31 19:14:22 +13:00
parent ebd88b3bb0
commit 65c092e4ee
Signed by: idanoo
GPG Key ID: 387387CDBC02F132
14 changed files with 293 additions and 397 deletions

View File

@ -8,6 +8,7 @@ variables:
build-go: build-go:
image: golang:1.16.2 image: golang:1.16.2
stage: build stage: build
only: master
script: script:
- go build -o goscrobble cmd/go-scrobble/*.go - go build -o goscrobble cmd/go-scrobble/*.go
artifacts: artifacts:
@ -21,6 +22,7 @@ build-go:
build-react: build-react:
image: node:15.12.0 image: node:15.12.0
stage: build stage: build
only: master
script: script:
- cd web - cd web
- npm install - npm install
@ -33,6 +35,7 @@ build-react:
bundle: bundle:
image: bash:latest image: bash:latest
stage: bundle stage: bundle
only: master
variables: variables:
GIT_STRATEGY: none GIT_STRATEGY: none
before_script: before_script:

View File

@ -1,5 +1,7 @@
# 0.0.7 # 0.0.7
-Switch redux -> context. - Switch redux -> Context
- Remove excess packages
# 0.0.6 # 0.0.6
- Fix hitting dashboard when logged out - Fix hitting dashboard when logged out

View File

@ -61,10 +61,8 @@ func createUser(req *RegisterRequest, ip net.IP) error {
// Check username is valid // Check username is valid
if !isUsernameValid(req.Username) { if !isUsernameValid(req.Username) {
log.Println("user is invalid")
return errors.New("Username contains invalid characters") return errors.New("Username contains invalid characters")
} }
log.Println("user is valid")
// If set an email.. validate it! // If set an email.. validate it!
if req.Email != "" { if req.Email != "" {

View File

@ -24,30 +24,26 @@ export const PostLogin = (formValues) => {
jwt: response.data.token, jwt: response.data.token,
uuid: expandedUser.sub, uuid: expandedUser.sub,
exp: expandedUser.exp, exp: expandedUser.exp,
username: expandedUser.username,
} }
// Set in local storage
localStorage.setItem('user', JSON.stringify(user));
// Set in context
// setUser(user)
toast.success('Successfully logged in.'); toast.success('Successfully logged in.');
// setLoading(false)
return user; return user;
} else { } else {
toast.error(response.data.error ? response.data.error: 'An Unknown Error has occurred'); toast.error(response.data.error ? response.data.error: 'An Unknown Error has occurred');
// setLoading(false)
return null return null
} }
}) })
.catch(() => {
return Promise.resolve();
});
}; };
export const PostRegister = (formValues) => { export const PostRegister = (formValues) => {
axios.post(process.env.REACT_APP_API_URL + "register", formValues) return axios.post(process.env.REACT_APP_API_URL + "register", formValues)
.then((response) => { .then((response) => {
if (response.data.token) { if (response.data.message) {
toast.success('Successfully registered. Please sign in'); toast.success(response.data.message);
return Promise.resolve(); return Promise.resolve();
} else { } else {
@ -56,16 +52,8 @@ export const PostRegister = (formValues) => {
return Promise.reject(); return Promise.reject();
} }
}) })
.error((error) => { .catch(() => {
const message = return Promise.resolve();
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
toast.error(message ? message : 'An Unknown Error has occurred')
return Promise.reject();
}); });
}; };

View File

@ -1,10 +1,10 @@
import { React, Component } from 'react'; import { React, useState, useContext } from 'react';
import { Navbar, NavbarBrand, Collapse, Nav, NavbarToggler, NavItem } from 'reactstrap'; import { Navbar, NavbarBrand, Collapse, Nav, NavbarToggler, NavItem } from 'reactstrap';
import { Link } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import logo from '../logo.png'; import logo from '../logo.png';
import './Navigation.css'; import './Navigation.css';
import logout from '../Contexts/AuthContextProvider'; import AuthContext from '../Contexts/AuthContext';
const menuItems = [ const menuItems = [
'Home', 'Home',
@ -20,178 +20,141 @@ const isMobile = () => {
return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
}; };
class Navigation extends Component { const Navigation = () => {
constructor(props) { const location = useLocation();
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.handleLogout = this.handleLogout.bind(this);
// Yeah I know you might not hit home.. but I can't get the // Lovely hack to highlight the current page (:
// path based finder thing working on initial load :sweatsmile: let active = "Home"
this.state = { active: "Home", collapsed: true }; if (location && location.pathname && location.pathname.length > 1) {
active = location.pathname.replace(/\//, "");
} }
componentDidMount() { let activeStyle = { color: '#FFFFFF' };
const { isLoggedIn } = this.props; let { user, Logout } = useContext(AuthContext);
if (isLoggedIn) { let [collapsed, setCollapsed] = useState(true);
this.setState({
isLoggedIn: true,
});
}
} const renderMobileNav = () => {
return <Navbar color="dark" dark fixed="top">
_handleClick(menuItem) { <NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
this.setState({ active: menuItem, collapsed: !this.state.collapsed }); <NavbarToggler onClick={setCollapsed(!collapsed)} className="mr-2" />
} <Collapse isOpen={!collapsed} navbar>
{user ?
handleLogout() { <Nav className="navLinkLoginMobile" navbar>
this.dispatch(logout()); {loggedInMenuItems.map(menuItem =>
} <NavItem>
<Link
toggleNavbar() { key={menuItem}
this.setState({ collapsed: !this.state.collapsed }); className="navLinkMobile"
} style={active === menuItem ? activeStyle : {}}
to={menuItem}
// This is a real mess. TO CLEAN UP. >{menuItem}</Link>
render() { </NavItem>
const activeStyle = { color: '#FFFFFF' }; )}
<Link
const renderMobileNav = () => { to="/profile"
return <Navbar color="dark" dark fixed="top"> style={active === "profile" ? activeStyle : {}}
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand> className="navLinkMobile"
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" /> >Profile</Link>
<Collapse isOpen={!this.state.collapsed} navbar> <Link to="/" className="navLink" onClick={Logout}>Logout</Link>
{this.state.isLoggedIn ? </Nav>
<Nav className="navLinkLoginMobile" navbar> : <Nav className="navLinkLoginMobile" navbar>
{loggedInMenuItems.map(menuItem => {menuItems.map(menuItem =>
<NavItem>
<Link
key={menuItem}
className="navLinkMobile"
style={active === menuItem ? activeStyle : {}}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
</NavItem>
)}
<NavItem> <NavItem>
<Link <Link
key={menuItem} to="/Login"
style={active === "Login" ? activeStyle : {}}
className="navLinkMobile" className="navLinkMobile"
style={this.state.active === menuItem ? activeStyle : {}} >Login</Link>
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem}
>{menuItem}</Link>
</NavItem> </NavItem>
)} <NavItem>
<Link
to="/Register"
className="navLinkMobile"
style={active === "Register" ? activeStyle : {}}
>Register</Link>
</NavItem>
</Nav>
}
</Collapse>
</Navbar>
}
const renderDesktopNav = () => {
return <Navbar color="dark" dark fixed="top">
<NavbarBrand className="mr-auto"><img src={logo} className="nav-logo" alt="logo" /> GoScrobble</NavbarBrand>
{user ?
<div>
{loggedInMenuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={active === menuItem ? activeStyle : {}}
to={menuItem}
>
{menuItem}
</Link>
)}
</div>
: <div>
{menuItems.map(menuItem =>
<Link
key={menuItem}
className="navLink"
style={active === menuItem ? activeStyle : {}}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
)}
</div>
}
{user ?
<div className="navLinkLogin">
<Link <Link
to="/profile" to="/profile"
style={this.state.active === "profile" ? activeStyle : {}} style={active === "profile" ? activeStyle : {}}
onClick={this._handleClick.bind(this, "profile")}
className="navLinkMobile"
>Profile</Link>
<Link to="/" className="navLink" onClick={this.handleLogout}>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 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" className="navLink"
style={this.state.active === menuItem ? activeStyle : {}} >{user.username}</Link>
onClick={this._handleClick.bind(this, menuItem)} <Link to="/" className="navLink" onClick={Logout}>Logout</Link>
to={menuItem} </div>
> :
{menuItem} <div className="navLinkLogin">
</Link> <Link
)} to="/login"
</div> style={active === "login" ? activeStyle : {}}
: <div> className="navLink"
{menuItems.map(menuItem => >Login</Link>
<Link <Link
key={menuItem} to="/register"
className="navLink" className="navLink"
style={this.state.active === menuItem ? activeStyle : {}} style={active === "register" ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)} >Register</Link>
to={menuItem === "Home" ? "/" : menuItem} </div>
>
{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={this.handleLogout}>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> </Navbar>
}
return (
<div>
{
isMobile()
? renderMobileNav()
: renderDesktopNav()
}
</div>
);
} }
return (
<div>
{
isMobile()
? renderMobileNav()
: renderDesktopNav()
}
</div>
);
} }
export default Navigation; export default Navigation;

View File

@ -1,15 +1,6 @@
import React from "react"; import React from "react";
class ScrobbleTable extends React.Component { const ScrobbleTable = (props) => {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
};
}
render() {
return ( return (
<div> <div>
<table border={1} cellPadding={5}> <table border={1} cellPadding={5}>
@ -23,8 +14,8 @@ class ScrobbleTable extends React.Component {
</thead> </thead>
<tbody> <tbody>
{ {
this.state.data && this.state.data.items && props.data && props.data.items &&
this.state.data.items.map(function (element) { props.data.items.map(function (element) {
return <tr> return <tr>
<td>{element.time}</td> <td>{element.time}</td>
<td>{element.track}</td> <td>{element.track}</td>
@ -37,7 +28,6 @@ class ScrobbleTable extends React.Component {
</table> </table>
</div> </div>
); );
}
} }
export default ScrobbleTable; export default ScrobbleTable;

View File

@ -1,7 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import AuthContext from './AuthContext'; import AuthContext from './AuthContext';
import { PostLogin, PostRegister } from '../Api/index'; import { PostLogin, PostRegister } from '../Api/index';
const AuthContextProvider = ({ children }) => { const AuthContextProvider = ({ children }) => {
@ -22,21 +21,16 @@ const AuthContextProvider = ({ children }) => {
PostLogin(formValues).then(user => { PostLogin(formValues).then(user => {
if (user) { if (user) {
setUser(user); setUser(user);
const { history } = this.props; localStorage.setItem('user', JSON.stringify(user));
history.push("/dashboard");
} }
setLoading(false); setLoading(false);
}) })
} }
const Register = (formValues) => { const Register = (formValues) => {
const { history } = this.props;
setLoading(true); setLoading(true);
return PostRegister(formValues).then(response => { return PostRegister(formValues).then(response => {
if (response) { // Do stuff here?
history.push("/login");
}
setLoading(false); setLoading(false);
}); });
}; };

View File

@ -12,7 +12,7 @@ const About = () => {
Used to track your listening history and build a profile to discover new music. Used to track your listening history and build a profile to discover new music.
</p> </p>
<a <a
className="aboutBodyw" className="aboutBody"
href="https://gitlab.com/idanoo/go-scrobble" href="https://gitlab.com/idanoo/go-scrobble"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"

View File

@ -1,56 +1,44 @@
import React from 'react'; import React, { useState, useEffect, useContext } from 'react';
import '../App.css'; import '../App.css';
import './Dashboard.css'; import './Dashboard.css';
import { useHistory } from "react-router";
import { getRecentScrobbles } from '../Api/index'; import { getRecentScrobbles } from '../Api/index';
import ScaleLoader from 'react-spinners/ScaleLoader'; import ScaleLoader from 'react-spinners/ScaleLoader';
import ScrobbleTable from "../Components/ScrobbleTable"; import ScrobbleTable from "../Components/ScrobbleTable";
import AuthContext from '../Contexts/AuthContext';
class Dashboard extends React.Component { const Dashboard = () => {
constructor(props) { const history = useHistory();
super(props); let { user } = useContext(AuthContext);
this.state = { let [isLoading, setIsLoading] = useState(true);
isLoading: true, let [dashboardData, setDashboardData] = useState({});
scrobbleData: [],
uuid: null, if (!user) {
}; history.push("/login");
} }
componentDidMount() { useEffect(() => {
const { history, uuid } = this.props; if (!user) {
const isLoggedIn = this.props.isLoggedIn; return
if (!isLoggedIn) {
history.push("/login")
} }
getRecentScrobbles(user.uuid)
.then(data => {
setDashboardData(data);
setIsLoading(false);
})
}, [user])
getRecentScrobbles(uuid) return (
.then((data) => { <div className="pageWrapper">
this.setState({ <h1>
isLoading: false, Dashboard!
data: data </h1>
}); {isLoading
}) ? <ScaleLoader color="#FFF" size={60} />
.catch(() => { : <ScrobbleTable data={dashboardData} />
this.setState({ }
isLoading: false </div>
}); );
});
}
render() {
return (
<div className="pageWrapper">
<h1>
Dashboard!
</h1>
{this.state.isLoading
? <ScaleLoader color="#FFF" size={60} />
: <ScrobbleTable data={this.state.data} />
}
</div>
);
}
} }
export default Dashboard; export default Dashboard;

View File

@ -4,16 +4,14 @@ import './Home.css';
import HomeBanner from '../Components/HomeBanner'; import HomeBanner from '../Components/HomeBanner';
import React from 'react'; import React from 'react';
class Home extends React.Component { const Home = () => {
render() { return (
return (
<div className="pageWrapper"> <div className="pageWrapper">
<img src={logo} className="App-logo" alt="logo" /> <img src={logo} className="App-logo" alt="logo" />
<p className="homeText">Go-Scrobble is an open source music scrobbling service written in Go and React.</p> <p className="homeText">Go-Scrobble is an open source music scrobbling service written in Go and React.</p>
<HomeBanner /> <HomeBanner />
</div> </div>
); );
}
} }
export default Home; export default Home;

View File

@ -5,10 +5,16 @@ import { Button } from 'reactstrap';
import { Formik, Form, Field } from 'formik'; import { Formik, Form, Field } from 'formik';
import ScaleLoader from 'react-spinners/ScaleLoader'; import ScaleLoader from 'react-spinners/ScaleLoader';
import AuthContext from '../Contexts/AuthContext'; import AuthContext from '../Contexts/AuthContext';
import { useHistory } from "react-router";
const Login = () => { const Login = () => {
const history = useHistory();
let boolTrue = true; let boolTrue = true;
let { Login, loading } = useContext(AuthContext); let { Login, loading, user } = useContext(AuthContext);
if (user) {
history.push("/dashboard");
}
return ( return (
<div className="pageWrapper"> <div className="pageWrapper">

View File

@ -1,25 +1,25 @@
import React from 'react'; import React, { useContext } from 'react';
import '../App.css'; import '../App.css';
import './Dashboard.css'; import './Dashboard.css';
import { useHistory } from "react-router";
import AuthContext from '../Contexts/AuthContext';
class Profile extends React.Component { const Profile = () => {
componentDidMount() { const history = useHistory();
const { history, isLoggedIn } = this.props; const { user } = useContext(AuthContext);
if (!isLoggedIn) { if (!user) {
history.push("/login") history.push("/login");
}
} }
render() { return (
return ( <div className="pageWrapper">
<div className="pageWrapper"> <h1>
<h1> Welcome {user.username}!
Hai User </h1>
</h1> </div>
</div> );
);
}
} }
export default Profile; export default Profile;

View File

@ -1,117 +1,90 @@
import React from 'react'; import React, { useContext } from 'react';
import '../App.css'; import '../App.css';
import './Register.css'; import './Register.css';
import { Button } from 'reactstrap'; import { Button } from 'reactstrap';
import ScaleLoader from "react-spinners/ScaleLoader"; import ScaleLoader from "react-spinners/ScaleLoader";
import register from '../Contexts/AuthContextProvider'; import AuthContext from '../Contexts/AuthContext';
import { Formik, Field, Form } from 'formik'; import { Formik, Field, Form } from 'formik';
import { useHistory } from "react-router";
class Register extends React.Component { const Register = () => {
constructor(props) { const history = useHistory();
super(props); let boolTrue = true;
this.state = {username: '', email: '', password: '', passwordconfirm: '', loading: false}; let { Register, user, loading } = useContext(AuthContext);
if (user) {
history.push("/dashboard");
} }
componentDidMount() { return (
const { history, isLoggedIn } = this.props; <div className="pageWrapper">
{
if (isLoggedIn) { // TODO: Move to DB:config REGISTRATION_DISABLED=1|0 :upsidedownsmile:
history.push("/dashboard") process.env.REACT_APP_REGISTRATION_DISABLED === "true" ?
} <p>Registration is temporarily disabled. Please try again soon!</p>
} :
<div>
handleRegister(values) { <h1>
console.log(values) Register
this.setState({loading: true}); </h1>
<div className="registerBody">
const { dispatch, history } = this.props; <Formik
initialValues={{ username: '', email: '', password: '', passwordconfirm: '' }}
dispatch(register(values.username, values.email, values.password)) onSubmit={async values => Register(values)}
.then(() => { >
this.setState({ <Form>
loading: false, <label>
}); Username*<br/>
history.push("/login"); <Field
}) name="username"
.catch(() => { type="text"
this.setState({ required={boolTrue}
loading: false className="registerFields"
}); />
}); </label>
} <br/>
<label>
render() { Email<br/>
let trueBool = true; <Field
return ( name="email"
<div className="pageWrapper"> type="email"
{ className="registerFields"
// TODO: Move to DB:config REGISTRATION_DISABLED=1|0 />
process.env.REACT_APP_REGISTRATION_DISABLED === "true" ? </label>
<p>Registration is temporarily disabled. Please try again soon!</p> <br/>
: <label>
<div> Password*<br/>
<h1> <Field
Register name="password"
</h1> type="password"
<div className="registerBody"> required={boolTrue}
<Formik className="registerFields"
initialValues={{ username: '', email: '', password: '', passwordconfirm: '' }} />
onSubmit={async values => this.handleRegister(values)} </label>
> <br/>
<Form> <label>
<label> Confirm Password*<br/>
Username*<br/> <Field
<Field name="passwordconfirm"
name="username" type="password"
type="text" required={boolTrue}
required={trueBool} className="registerFields"
className="registerFields" />
/> </label>
</label> <br/><br/>
<br/> <Button
<label> color="primary"
Email<br/> type="submit"
<Field className="registerButton"
name="email" disabled={loading}
type="email" >{loading ? <ScaleLoader color="#FFF" size={35} /> : "Register"}</Button>
className="registerFields" </Form>
/> </Formik>
</label>
<br/>
<label>
Password*<br/>
<Field
name="password"
type="password"
required={trueBool}
className="registerFields"
/>
</label>
<br/>
<label>
Confirm Password*<br/>
<Field
name="passwordconfirm"
type="password"
required={trueBool}
className="registerFields"
/>
</label>
<br/><br/>
<Button
color="primary"
type="submit"
className="registerButton"
disabled={this.state.loading}
>{this.state.loading ? <ScaleLoader color="#FFF" size={35} /> : "Register"}</Button>
</Form>
</Formik>
</div>
</div> </div>
} </div>
</div> }
); </div>
} );
} }
export default Register; export default Register;

View File

@ -2,26 +2,19 @@ import React from 'react';
import '../App.css'; import '../App.css';
import './Settings.css'; import './Settings.css';
class Settings extends React.Component { const Settings = () => {
constructor(props) { return (
super(props); <div className="pageWrapper">
this.state = {username: '', password: '', loading: false}; <h1>
} Settings
</h1>
render() { <div className="loginBody">
return ( <p>
<div className="pageWrapper"> All the settings
<h1> </p>
Settings
</h1>
<div className="loginBody">
<p>
All the settings
</p>
</div>
</div> </div>
); </div>
} );
} }
export default Settings; export default Settings;