import React, {useEffect, useState} from "react"; import {Link, useLocation, useNavigationType, useParams} from "react-router-dom"; import {useDispatch} from 'react-redux'; import {useNavigate} from 'react-router-dom'; import {BarLoader, MoonLoader} from "react-spinners"; import {usersApi} from '../utils/reducers/usersSlice'; import {workoutsApi} from '../utils/reducers/workoutsSlice'; import {competitionsApi} from '../utils/reducers/competitionsSlice'; import {statsApi} from '../utils/reducers/statsSlice'; import {feedApi} from '../utils/reducers/feedSlice'; import {PageWrapper} from "../utils/miscellaneous"; import {sentryError} from "../utils/reducers/baseQueryWithReauth"; function BaseHome({children}) { const navType = useNavigationType(); useEffect(() => { if (navType === "POP") { document.body.classList.remove("body-no-scroll"); } }, [navType]); return (

Workout Challenge

Compete with friends and co-workers across devices (Apple / Android / Garmin / etc.)
using the metrics you want to use (km / minutes / kcal / # of times / etc.)
respecting your privacy (no data is sold or shared and only for the competition necessary data synced with Strava)

{children}
) } function useWaitForLocalStorage(key, expectedValue, interval = 500) { const [matched, setMatched] = useState(false); useEffect(() => { const check = () => { const value = localStorage.getItem(key); if (value === expectedValue) { setMatched(true); } }; check(); // Initial check if (!matched) { const id = setInterval(() => { check(); }, interval); return () => clearInterval(id); } }, [key, expectedValue, matched]); return matched; } function LogoutPage() { const dispatch = useDispatch(); const navigate = useNavigate(); console.log('Clear localStorage as new user wants to register'); dispatch(usersApi.util.resetApiState()); dispatch(workoutsApi.util.resetApiState()); dispatch(competitionsApi.util.resetApiState()); dispatch(statsApi.util.resetApiState()); dispatch(feedApi.util.resetApiState()); localStorage.clear(); const matched = useWaitForLocalStorage("refresh_token", null); if (matched) { navigate("/login"); } ; return (
) } function WelcomePage() { const location = useLocation(); return (
Create Account Log In
); } const waitForLocalStorage = (key, timeout = 5000) => new Promise((resolve, reject) => { const start = Date.now(); const interval = setInterval(() => { const val = localStorage.getItem(key); if (val !== null) { clearInterval(interval); resolve(val); } else if (Date.now() - start > timeout) { clearInterval(interval); reject(new Error('Timeout waiting for localStorage key')); } }, 100); }); const LoadingForm = () => { return (
) } const apiCreateAccount = async (email, first_name, last_name, gender, password) => { try { const response = await fetch((process.env.REACT_APP_BACKEND_URL || '') + '/api/user/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email.toLowerCase(), first_name: first_name, last_name: last_name, gender: gender, password: password }), }); if (response.ok) { console.log('Registration Success'); return [true, undefined]; } else { console.log('Registration Error:', response.status, response.statusText); let error_msg = 'Registration Error (' + response.status + '): ' + response.statusText + ', '; try { const error = await response.json(); for (const key in error) { error_msg += key + ': ' + error[key] + ', '; } } catch (e) { error_msg += ' Unknown error'; } return [false, error_msg]; } } catch (error) { console.error('Network or server error during registration:', error); // Capture network errors in Sentry sentryError({ result: error, errorSource: 'manual-api', endpointName: 'register', }); return [false, 'Network or server error occurred. Please try again.']; } } const apiLogin = async (email, password) => { try { const response = await fetch((process.env.REACT_APP_BACKEND_URL || '') + '/api/token/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email.toLowerCase(), password: password }), }); if (response.ok) { console.log('Login Successful'); const token = await response.json(); localStorage.setItem('access_token', token.access); localStorage.setItem('refresh_token', token.refresh); return [true, undefined]; } else { console.log('Login Error:', response.status, response.statusText); let parsedError = null; try { parsedError = await response.json(); } catch (e) { parsedError = null; } return [false, response.statusText + ' (' + response.status + ') - ' + (parsedError ? parsedError.detail : 'Unknown error')]; } } catch (error) { console.error('Network or server error during login:', error); // Capture network errors in Sentry sentryError({ result: error, errorSource: 'manual-api', endpointName: 'login', }); return [false, 'Network or server error occurred. Please try again.']; } }; const apiRequestNewPassword = async (email) => { try { const response = await fetch((process.env.REACT_APP_BACKEND_URL || '') + '/api/password-reset/request/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email, }), }); if (response.ok) { console.log('Password Reset Request Successful'); return [true, undefined]; } else { console.log('Password Reset Request Error:', response.status, response.statusText, response); let parsedError = null; try { parsedError = await response.json(); } catch (e) { parsedError = null; } return [false, response.statusText + ' (' + response.status + ') - ' + (parsedError ? parsedError.detail : 'Unknown error')]; } } catch (error) { console.error('Network or server error during password reset request:', error); // Capture network errors in Sentry sentryError({ result: error, errorSource: 'manual-api', endpointName: 'new-password-request', }); return [false, 'Network or server error occurred. Please try again.']; } }; const apiSetNewPassword = async (uid, token, newPassword) => { try { const response = await fetch((process.env.REACT_APP_BACKEND_URL || '') + '/api/password-reset/confirm/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ uid: uid, token: token, new_password: newPassword, }), }); if (response.ok) { console.log('Set New Password Successful'); return [true, undefined]; } else { console.log('Set New Password Error:', response.status, response.statusText, response); let parsedError = null; try { parsedError = await response.json(); } catch (e) { parsedError = null; } return [false, response.statusText + ' (' + response.status + ') - ' + (parsedError ? parsedError.detail : 'Unknown error')]; } } catch (error) { console.error('Network or server error during password reset:', error); // Capture network errors in Sentry sentryError({ result: error, errorSource: 'manual-api', endpointName: 'set-new-password', }); return [false, 'Network or server error occurred. Please try again.']; } } const apiRefreshToken = async (refreshToken) => { try { const response = await fetch((process.env.REACT_APP_BACKEND_URL || '') + '/api/token/refresh/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ refresh: refreshToken, }), }); if (response.ok) { console.log('Token Refresh Successful'); const token = await response.json(); localStorage.setItem('access_token', token.access); return [true, undefined]; } else { console.log('Token Refresh Error:', response.status, response.statusText); let error = null; try { error = await response.json(); } catch (e) { error = { detail: 'Unknown error' }; } localStorage.removeItem('refresh_token'); return [false, response.statusText + ' (' + response.status + ') - ' + error.detail]; } } catch (error) { console.error('Network or server error during token refresh:', error); localStorage.removeItem('refresh_token'); // Capture network errors in Sentry sentryError({ result: error, errorSource: 'manual-api', endpointName: 'refresh-token', }); return [false, 'Network or server error occurred during token refresh. Please try again.']; } }; function RegisterPage() { const dispatch = useDispatch(); const location = useLocation(); const [isLoading, setIsLoading] = useState(false); const [errorMessage, setErrorMessage] = useState([]); const navigate = useNavigate(); async function handleSubmit(e) { e.preventDefault(); const email = e.target.email.value; const first_name = e.target.first_name.value; const last_name = e.target.last_name.value; const gender = e.target.gender.value; const password1 = e.target.password1.value; const password2 = e.target.password2.value; if (typeof (email) === "undefined" || email === null || email === "") { setErrorMessage(['Please enter an email address.']); } else if (typeof (first_name) === "undefined" || first_name === null || first_name === "") { setErrorMessage(['Please enter a first name.']); } else if (typeof (password1) === "undefined" || password1 === null || password1 === "") { setErrorMessage(['Please enter a password.']); } else if (password1 !== password2) { setErrorMessage(['Passwords do not match.']); } else { setIsLoading(true); const [success_register, msg_register] = await apiCreateAccount(email, first_name, last_name, gender, password1); const [success_login, msg_login] = await apiLogin(email, password1); const params = new URLSearchParams(location.search); params.set('welcome', 'true'); if (success_register && success_login) { await waitForLocalStorage('access_token'); console.log('Register and Login Successful - redirect ', localStorage.getItem('access_token')); navigate(`/dashboard/?${params.toString()}`); } else if (!success_register) { setErrorMessage(msg_register.split(", ")); } else if (!success_login) { setErrorMessage(['Successful Registration', 'Login ' + msg_login]); navigate(`/dashboard/?${params.toString()}`); } setIsLoading(false); } }; const [gender, setGender] = useState(''); const handleDropDownChange = (e) => { setGender(e.target.value); } useEffect(() => { console.log('Clear localStorage as new user wants to register'); dispatch(usersApi.util.resetApiState()); dispatch(workoutsApi.util.resetApiState()); dispatch(competitionsApi.util.resetApiState()); dispatch(statsApi.util.resetApiState()); dispatch(feedApi.util.resetApiState()); localStorage.clear(); }, []); return ( { isLoading ? : (
Go to SignIn

{errorMessage.map((item, index) => ( {item}
))}

)}
); } function LogInPage() { const dispatch = useDispatch(); const location = useLocation(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const params = new URLSearchParams(window.location.search); // handle submit/login action from login form async function handleSubmit(e) { e.preventDefault(); setErrorMessage(null); setIsLoading(true); const email = e.target.email.value; const password = e.target.password.value; const [success, msg] = await apiLogin(email, password); if (success) { // success logging in - redirect to dashboard await waitForLocalStorage('access_token'); setIsLoading(false); console.log('redirect', localStorage.getItem('access_token')); if (params.has('redirect')) { const redirectUrl = decodeURIComponent(params.get('redirect')); console.log('Redirect to:', redirectUrl); navigate(redirectUrl); } else { navigate(`/dashboard/${location.search}`); } } else { // error logging in - user try again setErrorMessage(msg); setIsLoading(false); } } // check if refreshToken already exists and user is already logged in async function checkRefreshToken(refreshToken) { console.log('refresh_token already exists - check if still valid'); setIsLoading(true); const [success, msg] = await apiRefreshToken(refreshToken); if (success) { // success refreshing access_token - redirecting to dashboard await waitForLocalStorage('access_token'); console.log('refresh_token exists and is valid - redirect ', localStorage.getItem('access_token')); navigate(`/dashboard/${location.search}`); } else { // error refreshing access_token - manual login required localStorage.removeItem('refresh_token'); console.log('refresh_token exists but expired - new login required'); } setIsLoading(false); } useEffect(() => { dispatch(usersApi.util.resetApiState()); dispatch(workoutsApi.util.resetApiState()); dispatch(competitionsApi.util.resetApiState()); dispatch(statsApi.util.resetApiState()); dispatch(feedApi.util.resetApiState()); const refreshToken = localStorage.getItem('refresh_token'); if (refreshToken !== null) { localStorage.removeItem('access_token'); checkRefreshToken(refreshToken); } }, []); return ( { isLoading ? : (
Forgot Password?
Create Account

{errorMessage}

) } }/> ); } function ResetPasswordPage() { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(''); // handle submit/reset action from reset password form async function handleSubmit(e) { e.preventDefault(); setErrorMessage(null); setIsLoading(true); const email = e.target.email.value; const [success, msg] = await apiRequestNewPassword(email); if (success) { // success reset request - redirect to start page window.alert('Success! Please check your email for a reset link.'); setIsLoading(false); console.log('redirect to login page'); navigate(`/`); } else { // error reset request - user try again setErrorMessage(msg); setIsLoading(false); } } return ( { isLoading ? : (
Back to SignIn

{ errorMessage }

)} }/> ); } function SetNewPasswordPage() { const {id, token} = useParams(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(''); // handle submit/reset action from reset password form async function handleSubmit(e) { e.preventDefault(); setErrorMessage(null); setIsLoading(true); const password1 = e.target.password1.value; const password2 = e.target.password2.value; if (typeof (password1) === "undefined" || password1 === null || password1 === "") { setErrorMessage(['Please enter a password.']); setIsLoading(false); } else if (password1 !== password2) { setErrorMessage(['Passwords do not match.']); setIsLoading(false); } else { const [success, msg] = await apiSetNewPassword(id, token, password1); if (success) { // success reset password - redirect to login page setIsLoading(false); console.log('redirect to login page'); navigate(`/login/`); } else { // error resetting password - user try again setErrorMessage(msg); setIsLoading(false); } } } return ( { isLoading ? : (

{ errorMessage }

)} }/> ); } // NotFound page const NotFound = () => { return (

404

Page Not Found

The page you're looking for doesn't exist or has been moved.

Go to Home
); }; export {WelcomePage, NotFound, RegisterPage, LogInPage, LogoutPage, ResetPasswordPage, SetNewPasswordPage};