1
0

Initial commit

This commit is contained in:
Eric Lewin
2020-08-23 21:48:06 -05:00
commit 162ce47bc6
57 changed files with 12096 additions and 0 deletions
@@ -0,0 +1,231 @@
import React, { useContext, useState, useEffect, useCallback } from "react";
import { AppContext } from "~/AppContext";
import styles from "../styles.css";
import { Line } from "react-chartjs-2";
import { format } from "date-fns";
import {
convertTemp,
convertLength,
convertSpeed,
} from "~/services/conversions";
import { fontColor } from "../common";
const createChartOptions = ({
darkMode,
tempUnit,
speedUnit,
lengthUnit,
altMode,
}) => {
return {
maintainAspectRatio: false,
legend: {
display: false,
},
responsive: true,
hoverMode: "index",
stacked: false,
title: {
display: true,
text: `7 Day ${
altMode
? `Wind Speed / Precipitation (${lengthUnit})`
: `Temp / Precipitation`
}`,
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
},
scales: {
xAxes: [
{
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
},
},
],
yAxes: [
{
type: "linear",
display: true,
position: "left",
id: "y-axis-1",
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
maxTicksLimit: 5,
callback: (val) => {
return altMode
? `${val} ${speedUnit === "mph" ? "mph" : "m/s"}`
: `${val} ${tempUnit.toUpperCase()}`;
},
},
},
{
type: "linear",
display: true,
position: "right",
id: "y-axis-2",
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
maxTicksLimit: 5,
suggestedMin: 0,
callback: (val) => {
return `${val}${altMode ? ` ${lengthUnit}` : "%"}`;
},
},
gridLines: {
drawOnChartArea: false,
},
},
],
},
};
};
const chartColors = {
blue: "rgba(63, 127, 191, 0.5)",
gray: "rgba(127, 127, 127, 0.5)",
gray2: "rgba(127, 127, 127, 0.3)",
};
const mapChartData = ({ data, tempUnit, speedUnit, altMode, lengthUnit }) => {
if (!data) {
return null;
}
return {
labels: data.map((e) => {
const date = new Date(e.observation_time.value);
const adjustedTimestamp =
date.getTime() + date.getTimezoneOffset() * 60 * 1000;
return format(new Date(adjustedTimestamp), "EEEEE");
}),
datasets: [
{
radius: 0,
label: altMode ? "High Wind Speed" : "High Temp",
data: data.map((e) => {
return altMode
? convertSpeed(e.wind_speed[1].max.value, speedUnit)
: convertTemp(e.temp[1].max.value, tempUnit);
}),
yAxisID: "y-axis-1",
borderColor: chartColors.gray,
backgroundColor: chartColors.gray,
fill: false,
},
{
radius: 0,
label: altMode ? "Low Wind Speed" : "Low Temp",
data: data.map((e) => {
return altMode
? convertSpeed(e.wind_speed[0].min.value, speedUnit)
: convertTemp(e.temp[0].min.value, tempUnit);
}),
yAxisID: "y-axis-1",
borderColor: chartColors.gray2,
backgroundColor: chartColors.gray2,
fill: false,
},
{
radius: 0,
label: "Precipitation",
data: data.map((e) => {
return altMode
? convertLength(e.precipitation[0].max.value, lengthUnit)
: e.precipitation_probability.value;
}),
yAxisID: "y-axis-2",
borderColor: chartColors.blue,
backgroundColor: chartColors.blue,
fill: false,
},
],
};
};
/**
* Daily forecast chart
*
* @returns {JSX.Element} Hourly forecast chart
*/
const DailyChart = () => {
const {
dailyWeatherData,
dailyWeatherDataErr,
dailyWeatherDataErrMsg,
tempUnit,
darkMode,
lengthUnit,
speedUnit,
} = useContext(AppContext);
const [altMode, setAltMode] = useState(false);
const [chartData, setChartData] = useState(null);
const [chartOptions, setChartOptions] = useState(null);
const setChartDataCallback = useCallback((e) => setChartData(e), []);
const setChartOptionsCallback = useCallback((e) => setChartOptions(e), []);
useEffect(() => {
if (dailyWeatherData) {
setChartDataCallback(
mapChartData({
data: dailyWeatherData,
tempUnit,
lengthUnit,
speedUnit,
altMode,
})
);
setChartOptionsCallback(
createChartOptions({
tempUnit,
darkMode,
lengthUnit,
speedUnit,
altMode,
})
);
}
}, [
dailyWeatherData,
tempUnit,
lengthUnit,
altMode,
speedUnit,
darkMode,
setChartOptionsCallback,
setChartDataCallback,
]);
if (chartData && chartOptions) {
return (
<div
className={styles.container}
onClick={() => {
setAltMode(!altMode);
}}
>
<Line options={chartOptions} data={chartData} />
</div>
);
} else if (dailyWeatherDataErr) {
return (
<div
className={`${darkMode ? styles.dark : styles.light} ${
styles.errContainer
}`}
>
<div>Cannot get 7 day weather forecast</div>
<div className={styles.message}>{dailyWeatherDataErrMsg}</div>
</div>
);
} else {
return null;
}
};
export default DailyChart;
@@ -0,0 +1,215 @@
import React, { useContext, useState, useEffect } from "react";
import { AppContext } from "~/AppContext";
import styles from "../styles.css";
import { Line } from "react-chartjs-2";
import { format } from "date-fns";
import {
convertTemp,
convertLength,
convertSpeed,
} from "~/services/conversions";
import { fontColor } from "../common";
const chartOptions = ({
darkMode,
tempUnit,
speedUnit,
lengthUnit,
altMode,
}) => {
return {
maintainAspectRatio: false,
legend: {
display: false,
},
responsive: true,
hoverMode: "index",
stacked: false,
title: {
display: true,
text: `24 Hour ${
altMode
? `Wind Speed / Precipitation (${lengthUnit})`
: `Temp / Precipitation`
}`,
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
},
scales: {
xAxes: [
{
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
},
},
],
yAxes: [
{
type: "linear",
display: true,
position: "left",
id: "y-axis-1",
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
maxTicksLimit: 5,
callback: (val) => {
return altMode
? `${val} ${speedUnit === "mph" ? "mph" : "m/s"}`
: `${val} ${tempUnit.toUpperCase()}`;
},
},
},
{
type: "linear",
display: true,
position: "right",
id: "y-axis-2",
ticks: {
fontColor: fontColor(darkMode),
fontFamily: "Rubik, sans-serif",
maxTicksLimit: 5,
suggestedMin: 0,
callback: (val) => {
return `${val}${altMode ? ` ${lengthUnit}` : "%"}`;
},
},
gridLines: {
drawOnChartArea: false,
},
},
],
},
};
};
const chartColors = {
blue: "rgba(63, 127, 191, 0.5)",
gray: "rgba(127, 127, 127, 0.5)",
};
const mapChartData = ({
data,
tempUnit,
speedUnit,
clockTime,
altMode,
lengthUnit,
}) => {
if (!data) {
return null;
}
return {
labels: data.map((e) => {
if (clockTime === "12") {
return `${format(new Date(e.observation_time.value), "h")}${format(
new Date(e.observation_time.value),
"aaaaa"
)}`;
} else {
return `${format(new Date(e.observation_time.value), "HH")}`;
}
}),
datasets: [
{
radius: 0,
label: altMode ? "Wind Speed" : "Temp",
data: data.map((e) => {
return altMode
? convertSpeed(e.wind_speed.value, speedUnit)
: convertTemp(e.temp.value, tempUnit);
}),
yAxisID: "y-axis-1",
borderColor: chartColors.gray,
backgroundColor: chartColors.gray,
fill: false,
},
{
radius: 0,
label: "Precipitation",
data: data.map((e) => {
return altMode
? convertLength(e.precipitation.value, lengthUnit)
: e.precipitation_probability.value;
}),
yAxisID: "y-axis-2",
borderColor: chartColors.blue,
backgroundColor: chartColors.blue,
fill: false,
},
],
};
};
/**
* Hourly forecast chart
*
* @returns {JSX.Element} Hourly forecast chart
*/
const HourlyChart = () => {
const {
hourlyWeatherData,
tempUnit,
darkMode,
clockTime,
lengthUnit,
speedUnit,
hourlyWeatherDataErr,
hourlyWeatherDataErrMsg,
} = useContext(AppContext);
const [altMode, setAltMode] = useState(false);
const [chartData, setChartData] = useState(null);
useEffect(() => {
if (hourlyWeatherData) {
setChartData(
mapChartData({
data: hourlyWeatherData,
tempUnit,
clockTime,
lengthUnit,
speedUnit,
altMode,
})
);
}
}, [hourlyWeatherData, tempUnit, clockTime, lengthUnit, altMode, speedUnit]);
if (chartData) {
return (
<div
className={styles.container}
onClick={() => {
setAltMode(!altMode);
}}
>
<Line
data={chartData}
options={chartOptions({
tempUnit,
darkMode,
lengthUnit,
speedUnit,
altMode,
})}
/>
</div>
);
}else if (hourlyWeatherDataErr) {
return (
<div
className={`${darkMode ? styles.dark : styles.light} ${
styles.errContainer
}`}
>
<div>Cannot get 24 hour weather forecast</div>
<div className={styles.message}>{hourlyWeatherDataErrMsg}</div>
</div>
);
} else {
return null;
}
};
export default HourlyChart;
@@ -0,0 +1,10 @@
/**
* Returns the appropriate font color depending on whether or not dark mode is enabled or not
* see `InfoPanel/styles.css`
*
* @param {Boolean} darkMode
* @returns {String} font color
*/
export const fontColor = (darkMode) => {
return darkMode ? "rgba(246, 246, 244, 0.8)" : "rgba(58, 57, 56, 0.8)";
};
@@ -0,0 +1,26 @@
.container {
width: 255px;
height: 115px;
}
.dark {
color: #f6f6f444;
}
.light {
color: #3a393888;
}
.err-container {
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
flex-direction: column;
}
.err-container .message{
margin-top: 10px;
word-break: break-word;
}