Skip to content

Commit

Permalink
feat: update landing page with API server status DOC-934
Browse files Browse the repository at this point in the history
This patch makes the following changes:
- Adds API connection status
- Add caching between renders with `useCallback`
- Small update to the README
  • Loading branch information
addetz committed Dec 20, 2023
1 parent f427508 commit 702d384
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 68 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ REACT_APP_API_URI=http://localhost:3000
REACT_APP_API_VERSION=1
```

The `.env` file is how you point to the local development API server. Otherwise, you will use the global API counter.
The `.env` file is how you point to the local development API server.


Use the [`docker-compose.yml`](./docker-compose.yml) to start the required services.
Expand Down Expand Up @@ -118,5 +118,4 @@ To start the Caddy server with a reverse proxy use the command `make start-proxy
## Dependencies

- [Caddy](https://caddyserver.com/docs/)
- [Count API](https://countapi.xyz/) is used to keep a global count of clicks.
- [React Spring](https://github.com/pmndrs/react-spring) is used to animate the logo.
10 changes: 9 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,19 @@ html {
color: #61dafb;
}

.Click-counter {
.Click-header {
font-family: Poppins,Arial,Helvetica,sans-serif,Russo One;
margin-bottom: 5%;
}

.Click-counter {
margin-bottom: 5%;
}

.Server-status {
font-size: calc(10px + 1vmin);
}

.App-footer {
min-height: 20vh;
}
Expand Down
148 changes: 83 additions & 65 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,110 +4,128 @@ import linkedin from "./img/linkedin.png";
import mastodon from "./img/mastodon.png";
import logo_text from "./img/logo_text.png";
import { FadeIn, SpinningComponent } from "./components/Animated/Animated";
import { useEffect, useState } from "react";
import countapi from "countapi-js";
import { useEffect, useState, useCallback } from "react";
import {randomLogo} from "./utilities/helpers";
import { env } from './env';
import { getCounter, postCounter } from "./utilities/requests";
import Menu from "./components/Animated/Menu/Menu";

const Status = {
OK: "OK",
Error: "Connection error",
NotSet: "No API configured"
}

function App() {
const countapiKey = "f3dceeba-1841-42cf-b76c-26e9026dc0cf"; // Yes, it's an API key but it's not a secret. It's used to identify the counter.
const countapiNamespace = "spectrocloud.com";
const [isLogoVisible, setIsLogoVisible] = useState(false);
const [clickCount, setClickCount] = useState(0);
const [connected, setConnected] = useState(false);
const [firstLoad, setFirstLoad] = useState(true);
const [apiStatus, setAPIStatus] = useState([Status.NotSet, null]);
let API_URI = env.REACT_APP_API_URI;
let API_VERSION = env.REACT_APP_API_VERSION;
let TOKEN = env.REACT_APP_TOKEN;

if (TOKEN == "undefined") {
if (TOKEN === "undefined") {
TOKEN = ""
}

if (API_URI == "undefined") {
if (API_URI === "undefined" || API_URI === undefined) {
API_URI = ""
}

if (API_VERSION === "" || API_VERSION == "undefined") {
if (API_VERSION === "" || API_VERSION === "undefined") {
API_VERSION = 1
}

useEffect(() => {
// Checks if internet is connected by attempting to load an image
const image = new Image();
image.onload = () => {setConnected(true)};
image.onerror = () => {setConnected(false)};
image.src = "https://www.google.com/images/phd/px.gif";

setIsLogoVisible(true);
loadCount();
}, [])

async function loadCount() {

// If an API URI is provided, use that to get the count
if (API_URI) {
let count;
try {
count = await getCounter(API_URI, API_VERSION, TOKEN);
if (count.message) {
count = 0;
throw new Error(`Error: Unable to connect to the API server on ${API_URI}. Please try again later. 😢`)
}
} catch(error) {
alert(`Error: Unable to connect to the API server on ${API_URI}. Please try again later. 😢`)
} finally {
setClickCount(count);
}
}


// If not connected to the internet, and no API URI is provided then use local storage for count.
if (API_URI == "" || API_URI === "undefined") {

// useCallback enables caching between re-renders
const connectionError = useCallback(()=> {
return new Error(`Error: Unable to connect to the API server on ${API_URI}. Please try again later. 😢`)
}, [API_URI])

const loadCount = useCallback(async () => {
// If no API URI is provided then use local storage for count.
if (API_URI === "") {
const count = localStorage.getItem("clickCount");
setClickCount(parseInt(count) || 0);
return
}
}

async function countUp() {

console.log("HERE")

// If an API URI is provided, use that to update the count
if (API_URI) {
let count;
try {
count = await postCounter(API_URI, API_VERSION, TOKEN);
if (count.message) {
count = 0;
throw new Error(`Error: Unable to connect to the API server on ${API_URI}. Please try again later. 😢`)
}
} catch (error) {
alert(`Error: Unable to connect to the API server on ${API_URI}. Please try again later. 😢`)
} finally {
setClickCount(count);
// An API URI must have been provided, use that to get the count
let count;
try {
count = await getCounter(API_URI, API_VERSION, TOKEN);
setAPIStatus([Status.OK, null])
if (count.message) {
count = 0;
setAPIStatus([Status.Error, connectionError()])
}
} catch(error) {
setAPIStatus([Status.Error, connectionError()])
} finally {
setClickCount(count);
}
}, [API_URI, API_VERSION, TOKEN, connectionError])

const countUp = useCallback(async () => {
if (firstLoad) {
setFirstLoad(false);
}

// If not connected to the internet, and no API URI is provided then use local storage for count.
if(API_URI == "" || API_URI === "undefined") {
// If no API URI is provided then use local storage for count.
if(API_URI === "") {
setClickCount(clickCount + 1);
localStorage.setItem("clickCount", clickCount);
return
}

if (firstLoad) {
setFirstLoad(false);
// An API URI must have been provided, use that to update the count
let count;
try {
count = await postCounter(API_URI, API_VERSION, TOKEN);
setAPIStatus([Status.OK, null])
if (count.message) {
count = 0;
setAPIStatus([Status.Error, connectionError()])
}
} catch (error) {
setAPIStatus([Status.Error, connectionError()])
} finally {
setClickCount(count);
}
}
}, [API_URI, API_VERSION, TOKEN, connectionError, clickCount, firstLoad])

const getCounterHeader = useCallback(()=> {
if (apiStatus[0] === Status.OK) {
return <div className="Click-header">
<div className="Click-counter">{`Click Count ${clickCount} 🤖 `}</div>
<div className="Server-status" > 🟢 API Server Connected </div>
</div>
}
if (apiStatus[0] === Status.Error) {
console.log(apiStatus[1])
return <div className="Click-header">
<div className="Click-counter">{`Click Count Not Available 🤖`}</div>
<div className="Server-status" > 🔴 API Server Connection Error </div>
</div>
}

return <div className="Click-header">
<div className="Click-counter">{`Click Count ${clickCount} 🌏`}</div>
<div className="Server-status" > ⚪️ API Server Not Configured </div>
</div>
}, [clickCount, apiStatus])

useEffect(() => {
setIsLogoVisible(true);
loadCount();
}, [loadCount])

return (
<div className="App">
<header className="App-header">
<Menu />
<div className="Header-items">
{ API_URI ? <span className="Click-counter">{`Clicked ${clickCount} 🤖 `}</span> : <span className="Click-counter">{`Clicked ${clickCount} 🌏`}</span>}
{getCounterHeader()}
<FadeIn isVisible={isLogoVisible}>
<SpinningComponent>
<div onClick={() => countUp()}>
Expand Down

0 comments on commit 702d384

Please sign in to comment.