Skip to content

Commit

Permalink
Fix Chanukah parsing logic + add new SVG
Browse files Browse the repository at this point in the history
+ and make it display correct number of candles for lighting!
  • Loading branch information
Arrow7000 committed Nov 28, 2020
1 parent b859ceb commit f6a3d20
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 133 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
"private": true,
"devDependencies": {
"parcel-bundler": "^1.12.3",
"react-scripts": "3.0.1"
},
"dependencies": {
"@sentry/browser": "^5.10.2",
"react-scripts": "3.0.1",
"@types/jest": "^23.3.9",
"@types/lodash": "^4.14.118",
"@types/node": "^10.12.9",
"@types/react": "^16.8.23",
"@types/react-dom": "^16.8.4",
"@types/styled-components": "^4.4.1",
"@types/styled-components": "^4.4.1"
},
"dependencies": {
"@sentry/browser": "^5.10.2",
"axios": "^0.18.0",
"lodash": "^4.17.11",
"moment": "^2.15.2",
Expand Down
92 changes: 51 additions & 41 deletions src/components/CandleCount/CandleCount.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { Component, useState, useEffect, FC } from "react";
import React, { useState, useEffect, FC } from "react";
import moment from "moment";
import { getTodayChanukahEvent } from "./CandleCountHelpers";
import { getCurrentPosition } from "./getCurrentPosition";
import { pluralise } from "./helpers";
import { pluralise } from "../../helpers";
import { Button } from "./Button";
import { CandleCountContainer } from "./CandleCountContainer";
import { captureException } from "@sentry/browser";
import { useLocalStorage } from "../../hooks/useLocalStorage";
import styled from "styled-components";
Expand All @@ -14,17 +13,18 @@ const prevAskedForGeoKey = "askedGeo";
enum NoGeoReason {
NotAsked,
Disallowed,
AllowedButUnable
AllowedButUnable,
}

/**
* Discriminated union allows attaching custom information for each state and
* makes illegal states unrepresentable
*/
type StateDiscrUnion =
type ChanukahState =
| { label: "NoGeo"; reason: NoGeoReason }
| { label: "ChanukahReqsInProgress" } // when cityName and lightingTime reqs are in progress
| { label: "ChanukahReqsFailed" }
| { label: "8thDayChanukah" }
| { label: "NotChanukah"; daysUntilChanukah: number }
| {
label: "ChanukahReqComplete";
Expand All @@ -38,7 +38,10 @@ const Text = styled.h3`
margin-bottom: 0.5em;
`;

function CandleCount() {
export const useGetCandleCount = (): [
state: ChanukahState,
askLocationPermission: VoidFunction
] => {
const maxAttempts = 3;
const [attemptsCount, setAttemptsCount] = useState(0);

Expand All @@ -48,11 +51,11 @@ function CandleCount() {

const [askedPermission, setAskedPermission] = useState(!!previouslyAsked);

const initialState: StateDiscrUnion = previouslyAsked
const initialState: ChanukahState = previouslyAsked
? { label: "ChanukahReqsInProgress" }
: { label: "NoGeo", reason: NoGeoReason.NotAsked };

const [state, setState] = useState<StateDiscrUnion>(initialState);
const [state, setState] = useState<ChanukahState>(initialState);

useEffect(() => {
if (askedPermission) {
Expand All @@ -67,7 +70,7 @@ function CandleCount() {
state.label === "ChanukahReqsFailed" &&
attemptsCount < maxAttempts
) {
setAttemptsCount(c => c + 1);
setAttemptsCount((c) => c + 1);

setTimeout(() => {
getLocationAndSetState();
Expand All @@ -93,7 +96,10 @@ function CandleCount() {

if (tonightChanukah.label === "Chanukah") {
setState({ ...tonightChanukah, label: "ChanukahReqComplete" });
} else if (tonightChanukah.label === "NotChanukah") {
} else if (
tonightChanukah.label === "NotChanukah" ||
tonightChanukah.label === "8thDayChanukah"
) {
setState(tonightChanukah);
}
} catch (error) {
Expand All @@ -111,14 +117,27 @@ function CandleCount() {
}
}

const askLocationPermission = () => setAskedPermission(true);
return [state, askLocationPermission];
};

interface CandleCountProps {
state: ChanukahState;
askLocationPermission: VoidFunction;
}

export const CandleCount: FC<CandleCountProps> = ({
state,
askLocationPermission,
}) => {
switch (state.label) {
case "NoGeo":
const { reason } = state;

switch (reason) {
case NoGeoReason.NotAsked:
return (
<Button onClick={() => setAskedPermission(true)}>
<Button onClick={askLocationPermission}>
Use your location to get candle lighting times
</Button>
);
Expand Down Expand Up @@ -156,6 +175,18 @@ function CandleCount() {
first night of Chanukah! 🕎
</Text>
);

case "8thDayChanukah":
return (
<>
<Text>
It's the 8th day of Chanukah which means no candles are lit tonight
😢
</Text>
<Text>We hope to see you next year! 👋</Text>
</>
);

case "ChanukahReqComplete":
const { candleLightingTime, cityName, candleCount } = state;
const displayCityName = cityName || `your location`;
Expand All @@ -176,46 +207,25 @@ function CandleCount() {
</Text>
<Text>
The Chanukah lighting time on Fridays is just before the time of
lighting Shabbat candles, the exact time of which will depend on
the local custom in {displayCityName}.
Shabbat candlesthe exact time of which depends on the local
custom in {displayCityName}.
</Text>
</>
);
}
}
}

/**
* Class component wrapper for error boundary
*/

export class CandleCountWithBoundary extends Component<
{},
{ hasError: boolean }
> {
state = { hasError: false };

static getDerivedStateFromError = () => ({ hasError: true });

render() {
return (
<CandleCountContainer>
{!this.state.hasError && <CandleCount />}
</CandleCountContainer>
);
}
}
};

/**
* @TODOs:
* - style geolocation button
* - style text
* - [x] style geolocation button
* - [x] style text
* - add different nusachim
* - get from rabbi roselaar's links
* - add nusach selector buttons
* - [x] get from rabbi roselaar's links
* - [x] add nusach selector buttons
* - make button clearer
* - maybe have popup to point towards nusach selector
* - make chanukah in svg
* - make chanukah display right number of candles
* - [x] make chanukah in svg
* - [x] make chanukah display right number of candles
* - add tests for new functions!
*/
27 changes: 17 additions & 10 deletions src/components/CandleCount/CandleCountHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import axios from "axios";
import { getInOrderOfPreference, momentDay } from "./helpers";
import { getInOrderOfPreference, momentDay } from "../../helpers";
import { captureException } from "@sentry/browser";

const chanukahRegex = /^Chanukah: (\d) Candles?: (\d\d?:\d\dpm)$/;
const candlesRegex = /^Chanukah: (\d) Candles?/;
const eighthDayRegex = /^Chanukah: 8th Day$/;

function getLightingInfoFromHebcalItem(item: HebCalItem): LightingInfo {
const regexResult = item.title.match(chanukahRegex);
const regexResult = item.title.match(candlesRegex);

if (regexResult) {
return {
count: parseInt(regexResult[1]),
// timeStr: regexResult[2],
lightingTime: new Date(item.date)
lightingTime: new Date(item.date),
};
} else {
throw new Error(
Expand All @@ -26,7 +26,7 @@ function getLightingInfoFromHebcalItem(item: HebCalItem): LightingInfo {

async function getLocationInfo({
latitude,
longitude
longitude,
}: Coordinates): Promise<GeocodingResult | null> {
const apiKey = "AIzaSyBOjCfj4s39EnTIynqE9n8VTG4BxwGxDKI";
const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${apiKey}`;
Expand Down Expand Up @@ -68,15 +68,20 @@ export async function getTodayChanukahEvent(
// change the 0 number to test various scenarios
const todayMoment = momentDay(today).add(0, "days");

const chanukahDatedItems = data.items.filter(item =>
item.title.match(chanukahRegex)
const chanukahDatedItems = data.items.filter((item) =>
item.title.match(candlesRegex)
);

const chanukahTodayOpt =
chanukahDatedItems.find(item =>
chanukahDatedItems.find((item) =>
momentDay(item.date).isSame(todayMoment, "day")
) || null;

const eighthDay = data.items.find((item) => item.title.match(eighthDayRegex));
const isEighthDay = eighthDay
? momentDay(eighthDay.date).isSame(todayMoment, "day")
: false;

if (chanukahTodayOpt) {
// It's Chanukah, bitches 🕎

Expand All @@ -94,8 +99,10 @@ export async function getTodayChanukahEvent(
candleLightingTime: itsFriday
? { day: "Friday" }
: { day: "Weekday", time: lightingInfo.lightingTime },
cityName
cityName,
};
} else if (isEighthDay) {
return { label: "8thDayChanukah" };
} else {
const diff = -todayMoment.diff(
momentDay(chanukahDatedItems[0].date),
Expand Down
21 changes: 0 additions & 21 deletions src/components/CandleCount/helpers.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/components/CandleCount/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type TonightChanukahData =
cityName: string | null;
candleLightingTime: { day: "Weekday"; time: Date } | { day: "Friday" };
}
| { label: "8thDayChanukah" }
| {
label: "NotChanukah";
daysUntilChanukah: number;
Expand Down
23 changes: 17 additions & 6 deletions src/components/MaozTzur.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react";
import "./MaozTzur.css";
import { content } from "../content";
import { Selector } from "./Selector";
import { CandleCountWithBoundary } from "./CandleCount/CandleCount";
import Menorah from "./Menorah";
import { CandleCount, useGetCandleCount } from "./CandleCount/CandleCount";
import { Menorah, numToDayMap } from "./Menorah";
import Section from "./Section";
import Footer from "./Footer";
import { isNusach } from "../helpers";
Expand Down Expand Up @@ -31,9 +31,11 @@ export function MaozTzur() {
const nusachim: Option<Nusach>[] = [
{ label: "Ashkenaz", value: "ashkenaz" },
{ label: "Sefardi", value: "sefardi" },
{ label: "Chabad", value: "chabad" }
{ label: "Chabad", value: "chabad" },
];

const [state, askLocationPermission] = useGetCandleCount();

useEffect(() => {
console.log(
"Hi there fellow developer 👋\nCheck out my other projects at https://github.com/Arrow7000"
Expand All @@ -43,9 +45,18 @@ export function MaozTzur() {
return (
<section className="MaozTzur">
<div className="MaozTzur__inner">
<Menorah />
<CandleCountWithBoundary />
{content.map(section => (
<Menorah
day={
state.label === "ChanukahReqComplete"
? numToDayMap[state.candleCount] ?? null
: null
}
/>
<CandleCount
state={state}
askLocationPermission={askLocationPermission}
/>
{content.map((section) => (
<Section key={section.title} section={section} nusach={nusach} />
))}
<Footer />
Expand Down
38 changes: 0 additions & 38 deletions src/components/Menorah.css

This file was deleted.

Loading

0 comments on commit f6a3d20

Please sign in to comment.