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

View File

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

View File

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

View File

@ -24,30 +24,26 @@ export const PostLogin = (formValues) => {
jwt: response.data.token,
uuid: expandedUser.sub,
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.');
// setLoading(false)
return user;
} else {
toast.error(response.data.error ? response.data.error: 'An Unknown Error has occurred');
// setLoading(false)
return null
}
})
.catch(() => {
return Promise.resolve();
});
};
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) => {
if (response.data.token) {
toast.success('Successfully registered. Please sign in');
if (response.data.message) {
toast.success(response.data.message);
return Promise.resolve();
} else {
@ -56,16 +52,8 @@ export const PostRegister = (formValues) => {
return Promise.reject();
}
})
.error((error) => {
const message =
(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();
.catch(() => {
return Promise.resolve();
});
};

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 { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
import logo from '../logo.png';
import './Navigation.css';
import logout from '../Contexts/AuthContextProvider';
import AuthContext from '../Contexts/AuthContext';
const menuItems = [
'Home',
@ -20,178 +20,141 @@ 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);
this.handleLogout = this.handleLogout.bind(this);
const Navigation = () => {
const location = useLocation();
// 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", collapsed: true };
// Lovely hack to highlight the current page (:
let active = "Home"
if (location && location.pathname && location.pathname.length > 1) {
active = location.pathname.replace(/\//, "");
}
componentDidMount() {
const { isLoggedIn } = this.props;
if (isLoggedIn) {
this.setState({
isLoggedIn: true,
});
}
let activeStyle = { color: '#FFFFFF' };
let { user, Logout } = useContext(AuthContext);
let [collapsed, setCollapsed] = useState(true);
}
_handleClick(menuItem) {
this.setState({ active: menuItem, collapsed: !this.state.collapsed });
}
handleLogout() {
this.dispatch(logout());
}
toggleNavbar() {
this.setState({ collapsed: !this.state.collapsed });
}
// This is a real mess. TO CLEAN UP.
render() {
const activeStyle = { color: '#FFFFFF' };
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 =>
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={setCollapsed(!collapsed)} className="mr-2" />
<Collapse isOpen={!collapsed} navbar>
{user ?
<Nav className="navLinkLoginMobile" navbar>
{loggedInMenuItems.map(menuItem =>
<NavItem>
<Link
key={menuItem}
className="navLinkMobile"
style={active === menuItem ? activeStyle : {}}
to={menuItem}
>{menuItem}</Link>
</NavItem>
)}
<Link
to="/profile"
style={active === "profile" ? activeStyle : {}}
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={active === menuItem ? activeStyle : {}}
to={menuItem === "Home" ? "/" : menuItem}
>
{menuItem}
</Link>
</NavItem>
)}
<NavItem>
<Link
key={menuItem}
to="/Login"
style={active === "Login" ? activeStyle : {}}
className="navLinkMobile"
style={this.state.active === menuItem ? activeStyle : {}}
onClick={this._handleClick.bind(this, menuItem)}
to={menuItem}
>{menuItem}</Link>
>Login</Link>
</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
to="/profile"
style={this.state.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}
style={active === "profile" ? activeStyle : {}}
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={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>
>{user.username}</Link>
<Link to="/" className="navLink" onClick={Logout}>Logout</Link>
</div>
:
<div className="navLinkLogin">
<Link
to="/login"
style={active === "login" ? activeStyle : {}}
className="navLink"
>Login</Link>
<Link
to="/register"
className="navLink"
style={active === "register" ? activeStyle : {}}
>Register</Link>
</div>
}
</Navbar>
}
return (
<div>
{
isMobile()
? renderMobileNav()
: renderDesktopNav()
}
</div>
);
}
</Navbar>
}
return (
<div>
{
isMobile()
? renderMobileNav()
: renderDesktopNav()
}
</div>
);
}
export default Navigation;

View File

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

View File

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

View File

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

View File

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

View File

@ -4,16 +4,14 @@ import './Home.css';
import HomeBanner from '../Components/HomeBanner';
import React from 'react';
class Home extends React.Component {
render() {
return (
const Home = () => {
return (
<div className="pageWrapper">
<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>
<HomeBanner />
</div>
);
}
}
export default Home;

View File

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

View File

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

View File

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

View File

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