module Reflex.Dom.Contrib.Geoposition where import Control.Concurrent (forkIO) import Control.Exception (catch, throwIO) import Control.Monad.IO.Class (liftIO) import Reflex (Event) import Reflex.Dom (MonadWidget) import Reflex.Dom.Class (performEventAsync) import GHCJS.DOM (currentWindow) import GHCJS.DOM.PositionError (PositionException(..), PositionErrorCode(..)) import qualified GHCJS.DOM.Coordinates as Coord import GHCJS.DOM.Geolocation (getCurrentPosition) import GHCJS.DOM.Geoposition (getCoords) import GHCJS.DOM.Navigator (getGeolocation) import GHCJS.DOM.Window (getNavigator) defaultPosException :: PositionException defaultPosException = PositionException PositionUnavailable "Failed to get geolocation" data GeopositionInfo = GeopositionInfo { geoLatitude :: Double , geoLongitude :: Double , geoAltitudeMeters :: Maybe Double , geoAccuracyMeters :: Double , geoAltitudeAccuracyMeters :: Maybe Double , geoHeadingDegrees :: Maybe Double , geoSpeedMetersPerSec :: Maybe Double } deriving (Show, Eq) getGeopositionInfo :: IO (Either PositionException GeopositionInfo) getGeopositionInfo = (Right <$> getInfo) `catch` (pure . Left) where getInfo = do window <- currentWindow >>= orBombOut nav <- getNavigator window >>= orBombOut geoloc <- getGeolocation nav >>= orBombOut geopos <- getCurrentPosition geoloc Nothing coord <- getCoords geopos >>= orBombOut GeopositionInfo <$> Coord.getLatitude coord <*> Coord.getLongitude coord <*> Coord.getAltitude coord <*> Coord.getAccuracy coord <*> Coord.getAltitudeAccuracy coord <*> Coord.getHeading coord <*> Coord.getSpeed coord orBombOut = maybe (throwIO defaultPosException) pure attachGeoposition :: MonadWidget t m => Event t a -> m (Event t (Either PositionException GeopositionInfo, a)) attachGeoposition event = performEventAsync (fetchInfoAsync <$> event) where fetchInfoAsync a callback = liftIO $ do _ <- forkIO $ do info <- getGeopositionInfo callback (info, a) pure ()