summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNick Van Doorn <vandoorn.nick@gmail.com>2019-03-29 00:35:56 -0700
committerNick Van Doorn <vandoorn.nick@gmail.com>2019-03-29 00:35:56 -0700
commit65220cc8db0d7c63f49627526466d54969958fe1 (patch)
treee2989cb16576f177e60f75f9a733487adfd868f5 /src
parent51687fe7a2de2d4ed2800af46f9b6a9ac6c929ee (diff)
Init commit
Diffstat (limited to 'src')
-rw-r--r--src/demo-component.js38
-rw-r--r--src/use-geolocation.js52
-rw-r--r--src/use-interval.js29
-rw-r--r--src/use-sunlight.js32
-rw-r--r--src/use-sunlight.test.js10
5 files changed, 161 insertions, 0 deletions
diff --git a/src/demo-component.js b/src/demo-component.js
new file mode 100644
index 0000000..ee83d83
--- /dev/null
+++ b/src/demo-component.js
@@ -0,0 +1,38 @@
+import React from 'react'
+import { useSunlight } from './use-sunlight'
+import colormap from 'colormap'
+
+const mainRange = colormap({
+ colormap: 'autumn',
+ nshades: 10,
+ format: 'hex',
+ alpha: 1
+})
+
+const backgroundRange = colormap({
+ colormap: 'winter',
+ nshades: 10,
+ format: 'hex',
+ alpha: 1
+})
+
+const sunlightToTheme = sunlightLevel => ({
+ main: mainRange[sunlightLevel],
+ background: backgroundRange[sunlightLevel]
+})
+
+const sunlightToStyle = theme => ({
+ background: theme.background,
+ color: theme.main,
+ minHeight: '500px',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center'
+})
+
+export const DemoComponent = ({ children }) => {
+ const [sunlight] = useSunlight()
+ const theme = sunlightToTheme(sunlight)
+ const style = sunlightToStyle(theme)
+ return <div style={style}>{children}</div>
+}
diff --git a/src/use-geolocation.js b/src/use-geolocation.js
new file mode 100644
index 0000000..9ab3f99
--- /dev/null
+++ b/src/use-geolocation.js
@@ -0,0 +1,52 @@
+// lifted from here @ b72c098:
+// https://github.com/streamich/react-use/blob/master/src/useGeolocation.ts
+import { useState, useEffect } from 'react'
+
+export const useGeolocation = () => {
+ const [state, setState] = useState({
+ loading: true,
+ accuracy: null,
+ altitude: null,
+ altitudeAccuracy: null,
+ heading: null,
+ latitude: null,
+ longitude: null,
+ speed: null,
+ timestamp: Date.now()
+ })
+ let mounted = true
+ let watchId: any
+
+ const onEvent = event => {
+ if (mounted) {
+ setState({
+ loading: false,
+ accuracy: event.coords.accuracy,
+ altitude: event.coords.altitude,
+ altitudeAccuracy: event.coords.altitudeAccuracy,
+ heading: event.coords.heading,
+ latitude: event.coords.latitude,
+ longitude: event.coords.longitude,
+ speed: event.coords.speed,
+ timestamp: event.timestamp
+ })
+ }
+ }
+ const onEventError = error =>
+ mounted && setState(oldState => ({ ...oldState, loading: false, error }))
+
+ useEffect(
+ () => {
+ navigator.geolocation.getCurrentPosition(onEvent, onEventError)
+ watchId = navigator.geolocation.watchPosition(onEvent, onEventError)
+
+ return () => {
+ mounted = false
+ navigator.geolocation.clearWatch(watchId)
+ }
+ },
+ [0]
+ )
+
+ return state
+}
diff --git a/src/use-interval.js b/src/use-interval.js
new file mode 100644
index 0000000..5ca3305
--- /dev/null
+++ b/src/use-interval.js
@@ -0,0 +1,29 @@
+// lifted from here:
+// https://overreacted.io/making-setinterval-declarative-with-react-hooks/
+import React, { useState, useEffect, useRef } from 'react'
+
+export const useInterval = (callback, delay) => {
+ const savedCallback = useRef()
+
+ // Remember the latest callback.
+ useEffect(
+ () => {
+ savedCallback.current = callback
+ },
+ [callback]
+ )
+
+ // Set up the interval.
+ useEffect(
+ () => {
+ function tick() {
+ savedCallback.current()
+ }
+ if (delay !== null) {
+ let id = setInterval(tick, delay)
+ return () => clearInterval(id)
+ }
+ },
+ [delay]
+ )
+}
diff --git a/src/use-sunlight.js b/src/use-sunlight.js
new file mode 100644
index 0000000..2ca279b
--- /dev/null
+++ b/src/use-sunlight.js
@@ -0,0 +1,32 @@
+import { useState } from 'react'
+import { useGeolocation as globalUseGeolocation } from './use-geolocation'
+import { useInterval } from './use-interval'
+import suncalc from 'suncalc'
+
+export const computeLightLevel = (
+ { loading, latitude, longitude },
+ date = new Date()
+) => {
+ if (loading || !latitude || !longitude) return 0
+ let { altitude } = suncalc.getPosition(date, latitude, longitude)
+ if (altitude < 0) {
+ altitude += 2 * Math.PI
+ }
+ // on or below the horizon is considered "no light"
+ if (altitude >= Math.PI) return 0
+ // otherwise, return a relative altitude
+ // where n is an int in [0,10]
+ // and 10 represents the zenith (straight over your head)
+ return Math.round(10 * Math.sin(altitude))
+}
+
+// make a location hook injectable such that we can test this
+export const useSunlight = (useGeolocation = globalUseGeolocation) => {
+ const geoLocation = useGeolocation()
+
+ const [lightLevel, setLightLevel] = useState(computeLightLevel(geoLocation))
+ useInterval(() => {
+ setLightLevel(computeLightLevel(geoLocation))
+ }, 1000)
+ return [lightLevel]
+}
diff --git a/src/use-sunlight.test.js b/src/use-sunlight.test.js
new file mode 100644
index 0000000..a4f4719
--- /dev/null
+++ b/src/use-sunlight.test.js
@@ -0,0 +1,10 @@
+import { computeLightLevel } from './use-sunlight'
+
+// Test the calculations used in the hook.
+// Not sure if it's worth testing the public hook
+// API
+test('computeLightLevel', () => {
+ expect(
+ computeLightLevel({ latitude: 0, longitude: 0, loading: false })
+ ).toMatchSnapshot()
+})