mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-01 05:32:18 +00:00
0.0.16
- Add registration_enabled to /api/v1/serverinfo - Add config table caching on save - Fix redis TTL not being parsed correctly - Move registration enabled to backend toggle - Fixed navbar when loading /u/profile URL - Token now shows on user page + can reset - Added basic popup validation to disconnect/reset buttons
This commit is contained in:
parent
f8bd321fbc
commit
9cbb94fc56
21 changed files with 246 additions and 59 deletions
|
@ -1,2 +1 @@
|
|||
REACT_APP_API_URL=http://127.0.0.1:42069/api/v1/
|
||||
REACT_APP_REGISTRATION_DISABLED=false
|
35
web/package-lock.json
generated
35
web/package-lock.json
generated
|
@ -20,6 +20,7 @@
|
|||
"jwt-decode": "^3.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-confirm-alert": "^2.7.0",
|
||||
"react-cookie": "^4.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
|
@ -27,6 +28,7 @@
|
|||
"react-spinners": "^0.10.6",
|
||||
"react-timezone-select": "^0.10.7",
|
||||
"react-toastify": "^7.0.3",
|
||||
"reactjs-popup": "^2.0.4",
|
||||
"reactstrap": "^8.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -16633,6 +16635,15 @@
|
|||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"node_modules/react-confirm-alert": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-confirm-alert/-/react-confirm-alert-2.7.0.tgz",
|
||||
"integrity": "sha512-21NWtGK/e85+ZX3TLRpMc3IsU5Kj6Z9ElCOrkTIlwMzV9EancyXNlkqHGbtKP63a2iS6g5hOxROokmJOqKQiXA==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.0.0",
|
||||
"react-dom": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-cookie": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
|
||||
|
@ -17101,6 +17112,18 @@
|
|||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reactjs-popup": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.4.tgz",
|
||||
"integrity": "sha512-G5jTXL2JkClKAYAdqedf+K9QvbNsFWvdbrXW1/vWiyanuCU/d7DtQzQux+uKOz2HeNVRsFQHvs7abs0Z7VLAhg==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/reactstrap": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.9.0.tgz",
|
||||
|
@ -35553,6 +35576,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-confirm-alert": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-confirm-alert/-/react-confirm-alert-2.7.0.tgz",
|
||||
"integrity": "sha512-21NWtGK/e85+ZX3TLRpMc3IsU5Kj6Z9ElCOrkTIlwMzV9EancyXNlkqHGbtKP63a2iS6g5hOxROokmJOqKQiXA==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-cookie": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.0.3.tgz",
|
||||
|
@ -35934,6 +35963,12 @@
|
|||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"reactjs-popup": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.4.tgz",
|
||||
"integrity": "sha512-G5jTXL2JkClKAYAdqedf+K9QvbNsFWvdbrXW1/vWiyanuCU/d7DtQzQux+uKOz2HeNVRsFQHvs7abs0Z7VLAhg==",
|
||||
"requires": {}
|
||||
},
|
||||
"reactstrap": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.9.0.tgz",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"jwt-decode": "^3.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-confirm-alert": "^2.7.0",
|
||||
"react-cookie": "^4.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
|
@ -22,6 +23,7 @@
|
|||
"react-spinners": "^0.10.6",
|
||||
"react-timezone-select": "^0.10.7",
|
||||
"react-toastify": "^7.0.3",
|
||||
"reactjs-popup": "^2.0.4",
|
||||
"reactstrap": "^8.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -243,3 +243,21 @@ export const spotifyDisonnectionRequest = () => {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
export const getServerInfo = () => {
|
||||
return axios.get(process.env.REACT_APP_API_URL + "serverinfo")
|
||||
.then((data) => {
|
||||
return data.data
|
||||
}).catch((error) => {
|
||||
return handleErrorResp(error)
|
||||
});
|
||||
}
|
||||
|
||||
export const resetScrobbleToken = () => {
|
||||
return axios.patch(process.env.REACT_APP_API_URL + "user", { token: "" }, { headers: getHeaders() })
|
||||
.then((data) => {
|
||||
return data.data
|
||||
}).catch((error) => {
|
||||
return handleErrorResp(error)
|
||||
});
|
||||
}
|
|
@ -24,7 +24,7 @@ const Navigation = () => {
|
|||
const location = useLocation();
|
||||
|
||||
// Lovely hack to highlight the current page (:
|
||||
let active = "Home"
|
||||
let active = "home"
|
||||
if (location && location.pathname && location.pathname.length > 1) {
|
||||
active = location.pathname.replace(/\//, "");
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ const Navigation = () => {
|
|||
<Link
|
||||
key={menuItem}
|
||||
className="navLinkMobile"
|
||||
style={active === menuItem ? activeStyle : {}}
|
||||
to={menuItem}
|
||||
style={active === menuItem.toLowerCase() ? activeStyle : {}}
|
||||
to={menuItem.toLowerCase()}
|
||||
onClick={toggleCollapsed}
|
||||
>{menuItem}</Link>
|
||||
</NavItem>
|
||||
|
@ -76,8 +76,8 @@ const Navigation = () => {
|
|||
<Link
|
||||
key={menuItem}
|
||||
className="navLinkMobile"
|
||||
style={active === menuItem ? activeStyle : {}}
|
||||
to={menuItem === "Home" ? "/" : menuItem}
|
||||
style={active === "home" && menuItem.toLowerCase() === "home" ? activeStyle : (active === menuItem.toLowerCase() ? activeStyle : {})}
|
||||
to={menuItem.toLowerCase() === "home" ? "/" : "/" + menuItem.toLowerCase()}
|
||||
onClick={toggleCollapsed}
|
||||
>{menuItem}
|
||||
</Link>
|
||||
|
@ -85,17 +85,17 @@ const Navigation = () => {
|
|||
)}
|
||||
<NavItem>
|
||||
<Link
|
||||
to="/Login"
|
||||
style={active === "Login" ? activeStyle : {}}
|
||||
to="/login"
|
||||
style={active === "login" ? activeStyle : {}}
|
||||
className="navLinkMobile"
|
||||
onClick={toggleCollapsed}
|
||||
>Login</Link>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<Link
|
||||
to="/Register"
|
||||
to="/register"
|
||||
className="navLinkMobile"
|
||||
style={active === "Register" ? activeStyle : {}}
|
||||
style={active === "register" ? activeStyle : {}}
|
||||
onClick={toggleCollapsed}
|
||||
>Register</Link>
|
||||
</NavItem>
|
||||
|
@ -114,8 +114,8 @@ const Navigation = () => {
|
|||
<Link
|
||||
key={menuItem}
|
||||
className="navLink"
|
||||
style={active === menuItem ? activeStyle : {}}
|
||||
to={menuItem}
|
||||
style={active === menuItem.toLowerCase() ? activeStyle : {}}
|
||||
to={"/" + menuItem.toLowerCase()}
|
||||
>
|
||||
{menuItem}
|
||||
</Link>
|
||||
|
@ -126,8 +126,8 @@ const Navigation = () => {
|
|||
<Link
|
||||
key={menuItem}
|
||||
className="navLink"
|
||||
style={active === menuItem ? activeStyle : {}}
|
||||
to={menuItem === "Home" ? "/" : menuItem}
|
||||
style={active === "home" && menuItem.toLowerCase() === "home" ? activeStyle : (active === menuItem.toLowerCase() ? activeStyle : {})}
|
||||
to={menuItem.toLowerCase() === "home" ? "/" : "/" + menuItem.toLowerCase()}
|
||||
>
|
||||
{menuItem}
|
||||
</Link>
|
||||
|
|
|
@ -17,8 +17,9 @@ const ScrobbleTable = (props) => {
|
|||
{
|
||||
props.data &&
|
||||
props.data.map(function (element) {
|
||||
let localTime = new Date(element.time);
|
||||
return <tr key={element.uuid}>
|
||||
<td>{element.time}</td>
|
||||
<td>{localTime.toLocaleString()}</td>
|
||||
<td>{element.track}</td>
|
||||
<td>{element.artist}</td>
|
||||
<td>{element.album}</td>
|
||||
|
|
|
@ -24,6 +24,7 @@ const Dashboard = () => {
|
|||
})
|
||||
}, [user])
|
||||
|
||||
|
||||
if (!user) {
|
||||
history.push("/login")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ const Profile = (route) => {
|
|||
|
||||
let username = false;
|
||||
if (route && route.match && route.match.params && route.match.params.uuid) {
|
||||
username = route.match.params.uuid
|
||||
username = route.match.params.uuid;
|
||||
} else {
|
||||
username = false;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1,16 +1,37 @@
|
|||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useState, useEffect } from 'react';
|
||||
import '../App.css';
|
||||
import './Register.css';
|
||||
import { Button } from 'reactstrap';
|
||||
import ScaleLoader from "react-spinners/ScaleLoader";
|
||||
import AuthContext from '../Contexts/AuthContext';
|
||||
import { Formik, Field, Form } from 'formik';
|
||||
import { useHistory } from "react-router";
|
||||
|
||||
import { useHistory } from 'react-router';
|
||||
import { getServerInfo } from '../Api/index';
|
||||
const Register = () => {
|
||||
const history = useHistory();
|
||||
let boolTrue = true;
|
||||
let { Register, user, loading } = useContext(AuthContext);
|
||||
let [serverInfo, setServerInfo] = useState({ registration_enabled: true });
|
||||
let [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
return
|
||||
}
|
||||
getServerInfo()
|
||||
.then(data => {
|
||||
setServerInfo(data);
|
||||
setIsLoading(false);
|
||||
})
|
||||
}, [user])
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="pageWrapper">
|
||||
<ScaleLoader color="#6AD7E5" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (user) {
|
||||
history.push("/dashboard");
|
||||
|
@ -19,8 +40,7 @@ const Register = () => {
|
|||
return (
|
||||
<div className="pageWrapper">
|
||||
{
|
||||
// TODO: Move to DB:config REGISTRATION_DISABLED=1|0 :upsidedownsmile:
|
||||
process.env.REACT_APP_REGISTRATION_DISABLED === "true" ?
|
||||
serverInfo.registration_enabled !== "1" ?
|
||||
<p>Registration is temporarily disabled. Please try again soon!</p>
|
||||
:
|
||||
<div>
|
||||
|
|
|
@ -6,4 +6,44 @@
|
|||
.userDropdown {
|
||||
color: #282C34;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.userButton {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
margin-top:-5px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
font-size: 12px;
|
||||
}
|
||||
.modal > .header {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid gray;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
}
|
||||
.modal > .content {
|
||||
width: 100%;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
.modal > .actions {
|
||||
width: 100%;
|
||||
padding: 10px 5px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
.modal > .close {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
display: block;
|
||||
padding: 2px 5px;
|
||||
line-height: 20px;
|
||||
right: -10px;
|
||||
top: -10px;
|
||||
font-size: 24px;
|
||||
background: #ffffff;
|
||||
border-radius: 18px;
|
||||
border: 1px solid #cfcece;
|
||||
}
|
|
@ -6,8 +6,9 @@ import AuthContext from '../Contexts/AuthContext';
|
|||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { getUser, patchUser } from '../Api/index'
|
||||
import { Button } from 'reactstrap';
|
||||
|
||||
import { spotifyConnectionRequest, spotifyDisonnectionRequest } from '../Api/index'
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import 'react-confirm-alert/src/react-confirm-alert.css';
|
||||
import { spotifyConnectionRequest, spotifyDisonnectionRequest, resetScrobbleToken } from '../Api/index'
|
||||
import TimezoneSelect from 'react-timezone-select'
|
||||
|
||||
const User = () => {
|
||||
|
@ -17,11 +18,54 @@ const User = () => {
|
|||
const [userdata, setUserdata] = useState({});
|
||||
|
||||
const updateTimezone = (vals) => {
|
||||
console.log(vals)
|
||||
setUserdata({...userdata, timezone: vals});
|
||||
patchUser({timezone: vals.value})
|
||||
}
|
||||
|
||||
const resetTokenPopup = () => {
|
||||
confirmAlert({
|
||||
title: 'Reset token',
|
||||
message: 'Resetting your token will require you to update your sources with the new token. Continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'Reset',
|
||||
onClick: () => resetToken()
|
||||
},
|
||||
{
|
||||
label: 'No',
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
const disconnectSpotifyPopup = () => {
|
||||
confirmAlert({
|
||||
title: 'Disconnect Spotify',
|
||||
message: 'Are you sure you want to disconnect your spotify account?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'Disconnect',
|
||||
onClick: () => spotifyDisonnectionRequest()
|
||||
},
|
||||
{
|
||||
label: 'No',
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
const resetToken = () => {
|
||||
setLoading(true);
|
||||
resetScrobbleToken(user.uuid)
|
||||
.then(() => {
|
||||
getUser()
|
||||
.then(data => {
|
||||
setUserdata(data);
|
||||
setLoading(false);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
return
|
||||
|
@ -58,23 +102,31 @@ const User = () => {
|
|||
value={userdata.timezone}
|
||||
onChange={updateTimezone}
|
||||
/><br/>
|
||||
Token: {userdata.token}<br/>
|
||||
<Button
|
||||
color="primary"
|
||||
type="button"
|
||||
className="userButton"
|
||||
onClick={resetTokenPopup}
|
||||
>Reset Token</Button><br/><br/>
|
||||
Created At: {userdata.created_at}<br/>
|
||||
Email: {userdata.email}<br/>
|
||||
Verified: {userdata.verified ? '✓' : '✖'}<br/>
|
||||
|
||||
{userdata.spotify_username
|
||||
? <div>Spotify Account: {userdata.spotify_username}<br/><br/>
|
||||
<Button
|
||||
color="secondary"
|
||||
type="button"
|
||||
className="loginButton"
|
||||
onClick={spotifyDisonnectionRequest}
|
||||
className="userButton"
|
||||
onClick={disconnectSpotifyPopup}
|
||||
>Disconnect Spotify</Button></div>
|
||||
: <div>
|
||||
<br/>
|
||||
<Button
|
||||
color="primary"
|
||||
type="button"
|
||||
className="loginButton"
|
||||
className="userButton"
|
||||
onClick={spotifyConnectionRequest}
|
||||
>Connect To Spotify</Button>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue