Skip to content

Commit

Permalink
Merge pull request #79 from agiledev-students-fall2023/minor-improvem…
Browse files Browse the repository at this point in the history
…ents-1

Various Minor Improvements
  • Loading branch information
unfiltered-syrup authored Nov 22, 2023
2 parents 73aa270 + 6385da1 commit ce41a67
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 141 deletions.
114 changes: 55 additions & 59 deletions front-end/src/App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useState, useEffect, useCallback, createContext } from 'react'; // Import useState and useEffect
import React, { useState, useEffect, createContext } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Import components
import MapPage from './components/MapPage';
import './css/navBar.css';
import NavBar from './components/NavBar';
import RoutesPage from './components/RoutesPage';
import RoutesSubpage from './components/RoutesSubpage';
Expand All @@ -13,86 +14,81 @@ import FeedbackSupportPage from './components/settings/FeedbackSupportPage';
import PrivacyPolicyPage from './components/settings/PrivacyPolicyPage';
import LoadingScreen from './components/LoadingScreen';
import TutorialComponent from './components/TutorialComponent';

// Import hooks and utilities
import useDarkMode from './hooks/darkMode';
import { registerService } from './utils/serviceRegister';
import { getUserPos } from './utils/mapUtility';
import { getUserPos, loadGoogleMapsAPI } from './utils/mapUtility';

// Import CSS
import './index.css';
import './css/navBar.css';
import './css/tutorialComponent.css';

export const TutorialContext = createContext(); // create context for tutorial that is shared between all pages
export const TutorialContext = createContext();

function App() {
const [colorTheme, setTheme] = useDarkMode();
const [isLoading, setIsLoading] = useState(true);
const [firstTime, setFirstTime] = useState(localStorage.getItem('isFirst')); // check if it is the first time the user is using the app
const [tutorialIndex, setTutorialIndex] = useState(0); // keep track which pages has been clicked on the tutorial
const [tutorialOn, setTutorialOn] = useState(false); // if tutorial is on
useEffect(() => {
localStorage.getItem('isFirst') == 'false' ? setFirstTime('false') : setFirstTime('true');
//get a list of all local storage items, for debugging purposes
const localStorageItems = {};
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
let value = localStorage.getItem(key);
localStorageItems[key] = value;
}
setTimeout(() => {
if (firstTime == 'true' || firstTime == null || firstTime == 'null') {
console.log('<--------First time user detected-------->');
console.log('Initializing local storage items...');
localStorage.setItem('isFirst', false); // set first time to false
} else {
console.log('<--------Returning user detected-------->');
console.log('Local storage items:');
console.log(localStorageItems);
}
setIsLoading(false);
}, 3000);
const isFirstTimeUser = localStorage.getItem('isFirst') !== 'false';
const [tutorialIndex, setTutorialIndex] = useState(0);
const [tutorialOn, setTutorialOn] = useState(isFirstTimeUser);
const localStorageItems = {};
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
let value = localStorage.getItem(key);
localStorageItems[key] = value;
}

window.addEventListener('keydown', devTools); // add button press even listeners for dev tools

// if first time is null, set it to true
useEffect(() => {
initializeLocalStorage(isFirstTimeUser);
loadGoogleMapsAPI(() => setIsLoading(false));
window.addEventListener('keydown', devTools);
registerService();
getUserPos();

return () => window.removeEventListener('keydown', devTools);
}, []);

useEffect(() => {
firstTime == 'true' ? setTutorialOn(true) : setTutorialOn(false);
}, [firstTime]);
const initializeLocalStorage = (isFirstTime) => {
if (isFirstTime) {
console.log('<--------First time user detected-------->');
console.log('Initializing local storage items...');
localStorage.setItem('isFirst', false);
} else {
console.log('<--------Returning user detected-------->');
console.log('Local storage items:', localStorageItems);
}
};

const devTools = (e) => {
switch (e.keyCode) {
case 82: //press r to reset local storage
console.log('Resetting local storage...');
localStorage.clear();
break;
if (e.keyCode === 82) {
// R key
console.log('Resetting local storage...');
localStorage.clear();
}
};

return (
<TutorialContext.Provider
value={{ tutorialIndex, setTutorialIndex, firstTime, setFirstTime, tutorialOn, setTutorialOn }}
>
{' '}
{/* Share the context without passing the prop to each page */}
<TutorialContext.Provider value={{ tutorialIndex, setTutorialIndex, tutorialOn, setTutorialOn }}>
<div onKeyDown={devTools}>
<BrowserRouter>
{!isLoading && tutorialOn && <TutorialComponent />}
{isLoading && <LoadingScreen />}{' '}
{/* Putting the loading component here so that loading screen appears when refreshing as well */}
{!isLoading && <NavBar />} {/* Hides navbar when loading */}
<Routes>
<Route path="/" element={<LoadingScreen />} /> {/*Goes to loading on app boot*/}
<Route path="/map" element={<MapPage />} />
<Route path="/routes" element={<RoutesPage />} />
<Route path="/alerts" element={<AlertsPage />} />
<Route path="/saved-routes" element={<SavedRoutesPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/settings/view-schedule" element={<TimeSpreadsheetPage />} />
<Route path="/settings/feedback-support" element={<FeedbackSupportPage />} />
<Route path="/settings/privacypolicy" element={<PrivacyPolicyPage />} />
<Route path="/routes/:location1/:location2" element={<RoutesSubpage />} />
</Routes>
{isLoading && <LoadingScreen />} {!isLoading && <NavBar />} {/* Hides navbar when loading */}
{!isLoading && (
<Routes>
<Route path="/" element={<MapPage />} />
<Route path="/map" element={<MapPage />} />
<Route path="/routes" element={<RoutesPage />} />
<Route path="/alerts" element={<AlertsPage />} />
<Route path="/saved-routes" element={<SavedRoutesPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/settings/view-schedule" element={<TimeSpreadsheetPage />} />
<Route path="/settings/feedback-support" element={<FeedbackSupportPage />} />
<Route path="/settings/privacypolicy" element={<PrivacyPolicyPage />} />
<Route path="/routes/:location1/:location2" element={<RoutesSubpage />} />
</Routes>
)}
</BrowserRouter>
</div>
</TutorialContext.Provider>
Expand Down
139 changes: 75 additions & 64 deletions front-end/src/components/NavBar.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,90 @@
import '../css/navBar.css'
import '../css/navBar.css';
import { ReactComponent as MapIcon } from '../images/map.svg';
import { ReactComponent as AlertIcon } from '../images/alert.svg';
import { ReactComponent as RouteIcon } from '../images/route.svg';
import { ReactComponent as SettingIcon } from '../images/gears.svg';
import { useState, useEffect , useContext } from 'react';
import { useState, useEffect, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { TutorialContext } from '../App';
import '../css/tutorialComponent.css'
import '../css/tutorialComponent.css';
import TutorialComponent from './TutorialComponent';

function NavBar() {
const [navBarState, setNavBarState] = useState('Map');
const {tutorialIndex, setTutorialIndex, firstTime, setFirstTime, tutorialOn, setTutorialOn} = useContext(TutorialContext);
const navigate = useNavigate();
const handleClick = (iconName) => {
setNavBarState(iconName);
}

useEffect(() => {
let overlay = document.getElementsByClassName('overlay')[0];
let cur = document.getElementById(navBarState);
if (cur.classList.contains('inactive')) { // when a inactive icon is clicked
document.getElementsByClassName('active')[0].classList.add('inactive');
document.getElementsByClassName('active')[0].classList.remove('active');
cur.classList.remove('inactive');
cur.classList.add('active');
switch(navBarState) { //change the functionalities of each button here
case 'Map':
overlay.style.left = '-36%'; //shifts the overlay
navigate('/map')
break;
case 'Routes':
overlay.style.left = '-12%';
navigate('/routes')
break;
case 'Alerts':
overlay.style.left = '12%';
navigate('/alerts')
break;
case 'Settings':
overlay.style.left = '36%';
navigate('/settings')
break;
}
}
}, [navBarState]);
const getPath = () => window.location.pathname.split('/')[1] || 'map';
const [navigationState, setnavigationState] = useState(getPath());
const { tutorialIndex, setTutorialIndex, firstTime, setFirstTime, tutorialOn, setTutorialOn } =
useContext(TutorialContext);
const navigate = useNavigate();
const handleClick = (iconName) => {
setnavigationState(iconName);
};

return (
<>
<div className="navBar">
<div className="backdrop"></div>
<div className="overlay"></div>
<div className="active" id="Map" onClick={() => handleClick('Map')}>
<MapIcon fill='#F0E9FF' width="25" height="25" aria-label="Map" />
<p>Map</p>
</div>
<div className="inactive" id="Routes" onClick={() => handleClick('Routes')}>
<RouteIcon width="25" height="25" aria-label="Routes" />
<p>Routes</p>
</div>
<div className="inactive" id="Alerts" onClick={() => handleClick('Alerts')}>
<AlertIcon width="25" height="25" aria-label="Alerts" />
<p className="flex">Alerts</p>
</div>
<div className="inactive" id="Settings" onClick={() => handleClick('Settings')}>
<SettingIcon width="25" height="25" aria-label="Settings" />
<p>Settings</p>
</div>
</div>
</>
);
}
const updateNavBarDisplay = (to) => {
const overlay = document.getElementsByClassName('overlay')[0];
const cur = document.getElementById(to);
if (cur.classList.contains('inactive')) {
// when a inactive icon is clicked
document.getElementsByClassName('active')[0].classList.add('inactive');
document.getElementsByClassName('active')[0].classList.remove('active');
cur.classList.remove('inactive');
cur.classList.add('active');

//change the functionalities of each button here
switch (to) {
case 'map':
overlay.style.left = '-36%'; //shifts the overlay
break;
case 'routes':
overlay.style.left = '-12%';
break;
case 'alerts':
overlay.style.left = '12%';
break;
case 'settings':
overlay.style.left = '36%';
break;
}
}
};

useEffect(() => {
updateNavBarDisplay(navigationState);
if (navigationState !== getPath()) {
navigate('/' + navigationState);
}
}, [navigationState]);

useEffect(() => {
// Send first time users to the map page
if (localStorage.getItem('isFirst') !== 'true') {
setnavigationState('map');
}
}, []);

return (
<>
<div className="navBar">
<div className="backdrop"></div>
<div className="overlay"></div>
<div className="active" id="map" onClick={() => handleClick('map')}>
<MapIcon fill="#F0E9FF" width="25" height="25" aria-label="Map" />
<p>Map</p>
</div>
<div className="inactive" id="routes" onClick={() => handleClick('routes')}>
<RouteIcon width="25" height="25" aria-label="Routes" />
<p>Routes</p>
</div>
<div className="inactive" id="alerts" onClick={() => handleClick('alerts')}>
<AlertIcon width="25" height="25" aria-label="Alerts" />
<p className="flex">Alerts</p>
</div>
<div className="inactive" id="settings" onClick={() => handleClick('settings')}>
<SettingIcon width="25" height="25" aria-label="Settings" />
<p>Settings</p>
</div>
</div>
</>
);
}

export default NavBar;
export default NavBar;
6 changes: 4 additions & 2 deletions front-end/src/utils/mapUtility.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@ const CALLBACK_NAME = 'gmapAPICallback';
const POS_DEFAULT = [40.716503, -73.976077];

export function loadGoogleMapsAPI(callback) {
let c = typeof callback == 'function' ? callback : () => {};

if (!window.google || !window.google.maps) {
window.gmapAPICallback = () => callback(true);
window.gmapAPICallback = () => c(true);

const script = document.createElement('script');
script.src = API_BASE + `?key=${API_KEY}&libraries=${API_LIBRARIES.join(',')}&callback=${CALLBACK_NAME}`;
script.async = true;
document.head.appendChild(script);
} else {
callback(true);
c(true);
}
}

Expand Down
Loading

0 comments on commit ce41a67

Please sign in to comment.