diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4c23cc07..c6537639 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ stages: - bundle variables: - VERSION: 0.0.15 + VERSION: 0.0.16 build-go: image: golang:1.16.2 diff --git a/docs/changelog.md b/docs/changelog.md index 58422c80..338106b5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,12 @@ +# 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 + # 0.0.15 - Fix spotify track duration diff --git a/docs/config.md b/docs/config.md index 2d4f67d9..2d7a2bf8 100644 --- a/docs/config.md +++ b/docs/config.md @@ -4,7 +4,6 @@ GoScrobble runs as UTC and connects to MySQL as UTC. All timezone handling is do ## FRONTEND VARS These are stored in `web/.env.production` and `web/.env.development` - REACT_APP_REGISTRATION_DISABLED=true // Disables registration REACT_APP_API_URL=https://goscrobble.com // Sets API URL diff --git a/internal/goscrobble/config.go b/internal/goscrobble/config.go index 9100a40e..fd952ec4 100644 --- a/internal/goscrobble/config.go +++ b/internal/goscrobble/config.go @@ -12,7 +12,8 @@ type Config struct { } type ServerInfo struct { - Version string `json:"version"` + Version string `json:"version"` + RegistrationEnabled string `json:"registration_enabled"` } func getAllConfigs() (Config, error) { diff --git a/internal/goscrobble/ingress_multiscrobbler.go b/internal/goscrobble/ingress_multiscrobbler.go index 408a7661..e82a05bc 100644 --- a/internal/goscrobble/ingress_multiscrobbler.go +++ b/internal/goscrobble/ingress_multiscrobbler.go @@ -2,7 +2,6 @@ package goscrobble import ( "database/sql" - "encoding/json" "errors" "fmt" "log" @@ -21,10 +20,9 @@ type MultiScrobblerRequest struct { // ParseMultiScrobblerInput - Transform API data func ParseMultiScrobblerInput(userUUID string, data MultiScrobblerRequest, ip net.IP, tx *sql.Tx) error { // Cache key - json, _ := json.Marshal(data) - redisKey := getMd5(string(json) + userUUID) + json := fmt.Sprintf("%s:%s:%s:%s", data.PlayedAt, data.Track, data.Album, userUUID) + redisKey := getMd5(json) if getRedisKeyExists(redisKey) { - fmt.Printf("Prevented duplicate entry!") return nil } @@ -63,8 +61,7 @@ func ParseMultiScrobblerInput(userUUID string, data MultiScrobblerRequest, ip ne return errors.New("Failed to map track") } - // Add cache key for the duration of the song *2 since we're caching the start time too - ttl := time.Duration(data.Duration*2) * time.Second + ttl := time.Duration(30) * time.Minute setRedisValTtl(redisKey, "1", ttl) return nil diff --git a/internal/goscrobble/redis.go b/internal/goscrobble/redis.go index 23a8ada8..b6e4eb3f 100644 --- a/internal/goscrobble/redis.go +++ b/internal/goscrobble/redis.go @@ -58,7 +58,7 @@ func setRedisVal(key string, val string) error { // setRedisTtl - Allows custom TTL func setRedisValTtl(key string, val string, ttl time.Duration) error { - return redisDb.Set(ctx, redisPrefix+key, val, 0).Err() + return redisDb.Set(ctx, redisPrefix+key, val, ttl).Err() } // getRedisVal - Returns value if exists diff --git a/internal/goscrobble/scrobble.go b/internal/goscrobble/scrobble.go index 42ce51ec..13615a96 100644 --- a/internal/goscrobble/scrobble.go +++ b/internal/goscrobble/scrobble.go @@ -54,16 +54,7 @@ func fetchScrobblesForUser(userUuid string, limit int, page int) (ScrobbleRespon // Yeah this isn't great. But for now.. it works! Cache later total, err := getDbCount( - "SELECT COUNT(*) FROM `scrobbles` "+ - "JOIN tracks ON scrobbles.track = tracks.uuid "+ - "JOIN track_artist ON track_artist.track = tracks.uuid "+ - "JOIN track_album ON track_album.track = tracks.uuid "+ - "JOIN artists ON track_artist.artist = artists.uuid "+ - "JOIN albums ON track_album.album = albums.uuid "+ - "JOIN users ON scrobbles.user = users.uuid "+ - "WHERE user = UUID_TO_BIN(?, true) "+ - "GROUP BY scrobbles.uuid", - userUuid) + "SELECT COUNT(*) FROM `scrobbles` WHERE `user` = UUID_TO_BIN(?, true) ", userUuid) if err != nil { log.Printf("Failed to fetch scrobble count: %+v", err) @@ -71,7 +62,7 @@ func fetchScrobblesForUser(userUuid string, limit int, page int) (ScrobbleRespon } rows, err := db.Query( - "SELECT BIN_TO_UUID(`scrobbles`.`uuid`, true), `scrobbles`.`created_at`, `artists`.`name`, `albums`.`name`,`tracks`.`name`, `scrobbles`.`source` FROM `scrobbles` "+ + "SELECT BIN_TO_UUID(`scrobbles`.`uuid`, true), `scrobbles`.`created_at`, GROUP_CONCAT(`artists`.`name` separator ','), `albums`.`name`, `tracks`.`name`, `scrobbles`.`source` FROM `scrobbles` "+ "JOIN tracks ON scrobbles.track = tracks.uuid "+ "JOIN track_artist ON track_artist.track = tracks.uuid "+ "JOIN track_album ON track_album.track = tracks.uuid "+ @@ -79,8 +70,10 @@ func fetchScrobblesForUser(userUuid string, limit int, page int) (ScrobbleRespon "JOIN albums ON track_album.album = albums.uuid "+ "JOIN users ON scrobbles.user = users.uuid "+ "WHERE user = UUID_TO_BIN(?, true) "+ + "GROUP BY scrobbles.uuid, albums.uuid "+ "ORDER BY scrobbles.created_at DESC LIMIT ?", userUuid, limit) + if err != nil { log.Printf("Failed to fetch scrobbles: %+v", err) return scrobbleReq, errors.New("Failed to fetch scrobbles") diff --git a/internal/goscrobble/server.go b/internal/goscrobble/server.go index 04c55189..0a648387 100644 --- a/internal/goscrobble/server.go +++ b/internal/goscrobble/server.go @@ -321,6 +321,9 @@ func patchUser(w http.ResponseWriter, r *http.Request, jwtUser string, reqUser s if isValidTimezone(val) { userFull.updateUser("timezone", val, ip) } + } else if k == "token" { + token := generateToken(32) + userFull.updateUser("token", token, ip) } } @@ -363,11 +366,13 @@ func postConfig(w http.ResponseWriter, r *http.Request, jwtUser string) { } for k, v := range bodyJson { - err = updateConfigValue(k, fmt.Sprintf("%s", v)) + val := fmt.Sprintf("%s", v) + err = updateConfigValue(k, val) if err != nil { throwOkError(w, err.Error()) return } + setRedisVal(k, val) } throwOkMessage(w, "Config updated successfully") @@ -446,8 +451,19 @@ func deleteSpotifyLink(w http.ResponseWriter, r *http.Request, u string, v strin } func fetchServerInfo(w http.ResponseWriter, r *http.Request) { + cachedRegistrationEnabled := getRedisVal("REGISTRATION_ENABLED") + if cachedRegistrationEnabled == "" { + registrationEnabled, err := getConfigValue("REGISTRATION_ENABLED") + if err != nil { + throwOkError(w, "Error fetching serverinfo") + } + setRedisVal("REGISTRATION_ENABLED", registrationEnabled) + cachedRegistrationEnabled = registrationEnabled + } + info := ServerInfo{ - Version: "0.0.15", + Version: "0.0.16", + RegistrationEnabled: cachedRegistrationEnabled, } js, _ := json.Marshal(&info) diff --git a/internal/goscrobble/user.go b/internal/goscrobble/user.go index 3d9722e1..5c7ec814 100644 --- a/internal/goscrobble/user.go +++ b/internal/goscrobble/user.go @@ -29,6 +29,7 @@ type User struct { Active bool `json:"active"` Admin bool `json:"admin"` Timezone string `json:"timezone"` + Token string `json:"token"` } type UserResponse struct { @@ -42,6 +43,7 @@ type UserResponse struct { Verified bool `json:"verified"` SpotifyUsername string `json:"spotify_username"` Timezone string `json:"timezone"` + Token string `json:"token"` } // RegisterRequest - Incoming JSON @@ -211,8 +213,8 @@ func userAlreadyExists(req *RegisterRequest) bool { func getUser(uuid string) (User, error) { var user User - err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone` FROM `users` WHERE `uuid` = UUID_TO_BIN(?, true) AND `active` = 1", - uuid).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone) + err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `uuid` = UUID_TO_BIN(?, true) AND `active` = 1", + uuid).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token) if err == sql.ErrNoRows { return user, errors.New("Invalid JWT Token") @@ -223,8 +225,8 @@ func getUser(uuid string) (User, error) { func getUserByUsername(username string) (User, error) { var user User - err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone` FROM `users` WHERE `username` = ? AND `active` = 1", - username).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone) + err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `username` = ? AND `active` = 1", + username).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token) if err == sql.ErrNoRows { return user, errors.New("Invalid Username") @@ -235,8 +237,8 @@ func getUserByUsername(username string) (User, error) { func getUserByEmail(email string) (User, error) { var user User - err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone` FROM `users` WHERE `email` = ? AND `active` = 1", - email).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone) + err := db.QueryRow("SELECT BIN_TO_UUID(`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` WHERE `email` = ? AND `active` = 1", + email).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token) if err == sql.ErrNoRows { return user, errors.New("Invalid Email") @@ -247,9 +249,9 @@ func getUserByEmail(email string) (User, error) { func getUserByResetToken(token string) (User, error) { var user User - err := db.QueryRow("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, timezone FROM `users` "+ + err := db.QueryRow("SELECT BIN_TO_UUID(`users`.`uuid`, true), `created_at`, `created_ip`, `modified_at`, `modified_ip`, `username`, `email`, `password`, `verified`, `admin`, `timezone`, `token` FROM `users` "+ "JOIN `resettoken` ON `resettoken`.`user` = `users`.`uuid` WHERE `resettoken`.`token` = ? AND `active` = 1", - token).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone) + token).Scan(&user.UUID, &user.CreatedAt, &user.CreatedIp, &user.ModifiedAt, &user.ModifiedIP, &user.Username, &user.Email, &user.Password, &user.Verified, &user.Admin, &user.Timezone, &user.Token) if err == sql.ErrNoRows { return user, errors.New("Invalid Token") diff --git a/migrations/2_users.up.sql b/migrations/2_users.up.sql index 44bea470..469a2f46 100644 --- a/migrations/2_users.up.sql +++ b/migrations/2_users.up.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS `users` ( `active` TINYINT(1) NOT NULL DEFAULT 1, `admin` TINYINT(1) NOT NULL DEFAULT 0, `private` TINYINT(1) NOT NULL DEFAULT 0, - `timezone` VARCHAR(100) NOT NULL DEFAULT 'Etc/UTC', + `timezone` VARCHAR(100) NOT NULL DEFAULT 'Pacific/Auckland', KEY `usernameLookup` (`username`, `active`), KEY `emailLookup` (`email`, `active`) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/web/.env.example b/web/.env.example index cfed93ef..fbedc8f2 100644 --- a/web/.env.example +++ b/web/.env.example @@ -1,2 +1 @@ REACT_APP_API_URL=http://127.0.0.1:42069/api/v1/ -REACT_APP_REGISTRATION_DISABLED=false \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index a9801c9f..dbf5b788 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -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", diff --git a/web/package.json b/web/package.json index d781bbc9..ef773d6a 100644 --- a/web/package.json +++ b/web/package.json @@ -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": { diff --git a/web/src/Api/index.js b/web/src/Api/index.js index 1b64cc95..0e0a53e3 100644 --- a/web/src/Api/index.js +++ b/web/src/Api/index.js @@ -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) + }); +} \ No newline at end of file diff --git a/web/src/Components/Navigation.js b/web/src/Components/Navigation.js index fdf6eb24..e625b218 100644 --- a/web/src/Components/Navigation.js +++ b/web/src/Components/Navigation.js @@ -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 = () => { {menuItem} @@ -76,8 +76,8 @@ const Navigation = () => { {menuItem} @@ -85,17 +85,17 @@ const Navigation = () => { )} Login Register @@ -114,8 +114,8 @@ const Navigation = () => { {menuItem} @@ -126,8 +126,8 @@ const Navigation = () => { {menuItem} diff --git a/web/src/Components/ScrobbleTable.js b/web/src/Components/ScrobbleTable.js index 3a97a800..2c18c0c8 100644 --- a/web/src/Components/ScrobbleTable.js +++ b/web/src/Components/ScrobbleTable.js @@ -17,8 +17,9 @@ const ScrobbleTable = (props) => { { props.data && props.data.map(function (element) { + let localTime = new Date(element.time); return - {element.time} + {localTime.toLocaleString()} {element.track} {element.artist} {element.album} diff --git a/web/src/Pages/Dashboard.js b/web/src/Pages/Dashboard.js index 1cfcde59..069ab202 100644 --- a/web/src/Pages/Dashboard.js +++ b/web/src/Pages/Dashboard.js @@ -24,6 +24,7 @@ const Dashboard = () => { }) }, [user]) + if (!user) { history.push("/login") } diff --git a/web/src/Pages/Profile.js b/web/src/Pages/Profile.js index fe44a506..ca95aa42 100644 --- a/web/src/Pages/Profile.js +++ b/web/src/Pages/Profile.js @@ -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(() => { diff --git a/web/src/Pages/Register.js b/web/src/Pages/Register.js index 9787ebb6..0e1bda94 100644 --- a/web/src/Pages/Register.js +++ b/web/src/Pages/Register.js @@ -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 ( +
+ +
+ ) + } if (user) { history.push("/dashboard"); @@ -19,8 +40,7 @@ const Register = () => { return (
{ - // TODO: Move to DB:config REGISTRATION_DISABLED=1|0 :upsidedownsmile: - process.env.REACT_APP_REGISTRATION_DISABLED === "true" ? + serverInfo.registration_enabled !== "1" ?

Registration is temporarily disabled. Please try again soon!

:
diff --git a/web/src/Pages/User.css b/web/src/Pages/User.css index 7d8a1e75..4c4e8de7 100644 --- a/web/src/Pages/User.css +++ b/web/src/Pages/User.css @@ -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; } \ No newline at end of file diff --git a/web/src/Pages/User.js b/web/src/Pages/User.js index a5077fd8..312030c8 100644 --- a/web/src/Pages/User.js +++ b/web/src/Pages/User.js @@ -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} />
+ Token: {userdata.token}
+

Created At: {userdata.created_at}
Email: {userdata.email}
Verified: {userdata.verified ? '✓' : '✖'}
+ {userdata.spotify_username ?
Spotify Account: {userdata.spotify_username}

: