diff options
author | Nick Van Doorn <vandoorn.nick@gmail.com> | 2019-03-29 00:35:56 -0700 |
---|---|---|
committer | Nick Van Doorn <vandoorn.nick@gmail.com> | 2019-03-29 00:35:56 -0700 |
commit | 65220cc8db0d7c63f49627526466d54969958fe1 (patch) | |
tree | e2989cb16576f177e60f75f9a733487adfd868f5 /src | |
parent | 51687fe7a2de2d4ed2800af46f9b6a9ac6c929ee (diff) |
Init commit
Diffstat (limited to 'src')
-rw-r--r-- | src/demo-component.js | 38 | ||||
-rw-r--r-- | src/use-geolocation.js | 52 | ||||
-rw-r--r-- | src/use-interval.js | 29 | ||||
-rw-r--r-- | src/use-sunlight.js | 32 | ||||
-rw-r--r-- | src/use-sunlight.test.js | 10 |
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() +}) |