Skip to content

Commit 24bd0cf

Browse files
committed
Modernize and simplify API
1 parent ae3dde1 commit 24bd0cf

File tree

2 files changed

+145
-90
lines changed

2 files changed

+145
-90
lines changed

src/Geolocation.elm

Lines changed: 98 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,145 @@
11
module Geolocation
2-
( Position, Coords
3-
, currentPosition
4-
, watchPosition, clearWatch
5-
, Options, defaultOptions
2+
( Location
3+
, current
4+
, subscribe
5+
, unsubscribe
6+
, Options
7+
, defaultOptions
68
, Error(..)
7-
) where
9+
)
10+
where
811

9-
import Native.Geolocation
10-
import Promise (Promise)
11-
import Time (Time)
12+
{-| Primitive bindings to the web's [Geolocation API][geo]. You probably want
13+
to use something higher-level than this, like the elm-effects API for
14+
geolocation.
15+
16+
[geo]: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
1217
13-
{-|
18+
# Location
19+
@docs Location
1420
15-
# Current Position
16-
@docs currentPosition, Options, defaultOptions, Position, Coords, Error
21+
# Requesting a Location
22+
@docs current, subscribe, unsubscribe
1723
18-
# Watch Position Over Time
19-
@docs watchPosition, clearWatch
24+
# Options
25+
@docs Options, defaultOptions
26+
27+
# Errors
28+
@docs Error
2029
2130
-}
2231

23-
type alias Position =
24-
{ coords : Coords
25-
, timestamp : Time
26-
}
32+
33+
import Native.Geolocation
34+
import Task exposing (Task)
35+
import Time exposing (Time)
2736

2837

29-
{-| Coordinate data provides as much information as possible for a given
30-
device.
38+
{-| All available details of the device's current location in the world.
3139
3240
* `latitude` — the latitude in decimal degrees.
3341
* `longitude` — the longitude in decimal degrees.
34-
* `altitude` — the altitude in meters, relative to sea level, if the
35-
implementation cannot provide the data.
36-
* `accuracy` — the accuracy of the latitude and longitude properties,
37-
expressed in meters.
38-
* `altitudeAccuracy` — the accuracy of the altitude expressed in
39-
meters, if available.
40-
* `heading` — the direction in which the device is traveling, if
41-
available. This value, specified in degrees, indicates how far off from
42-
heading due north the device is. 0 degrees represents true north, 90
43-
degrees is east, 270 degrees is west, and everything in between. If speed
44-
is 0, heading is NaN.
42+
* `accuracy` — the accuracy of the latitude and longitude, expressed in meters.
43+
* `altitude` — the altitude in meters relative to sea level, if available.
44+
* `altitudeAccuracy` — the accuracy of the altitude expressed in meters, if available.
4545
* `speed` — the velocity of the device in meters per second, if available.
46+
* `heading` — the direction in which the device is traveling, if available.
47+
This value, specified in degrees, indicates how far off from heading due north
48+
the device is. 0 degrees represents true north, 90 degrees is east, 270 degrees
49+
is west, and everything in between. If speed is 0, heading is NaN.
50+
* `timestamp` — the time that this location reading was taken in milliseconds.
4651
-}
47-
type alias Coords =
52+
type alias Location =
4853
{ latitude : Float
4954
, longitude : Float
50-
, altitude : Maybe Float
5155
, accuracy : Float
56+
, altitude : Maybe Float
5257
, altitudeAccuracy : Maybe Float
53-
, heading : Maybe Float
5458
, speed : Maybe Float
59+
, heading : Maybe Float
60+
, timestamp : Time
5561
}
5662

5763

64+
{-| The `current` and `subscribe` functions may fail for a variaty of reasons.
65+
66+
* The user may reject the request to use their location.
67+
* It may be impossible to get a location.
68+
* If you set a timeout in the `Options` the request may just take to long.
69+
70+
In each case, the browser will provide a string with additional information.
71+
-}
5872
type Error
5973
= PermissionDenied String
60-
| PositionUnavailable String
74+
| LocationUnavailable String
6175
| Timeout String
6276

6377

64-
currentPosition : Options -> Promise Error Position
65-
currentPosition =
66-
Native.Geolocation.currentPosition
78+
{-| Request the current position of the user's device. On the first request,
79+
the user will need to give permission to access this information. A typical
80+
request would look like this:
6781
82+
current defaultOptions
83+
-}
84+
current : Options -> Task Error Location
85+
current =
86+
Native.Geolocation.current
6887

69-
watchPosition : Options -> (Position -> Promise x a) -> (Error -> Promise y b) -> Promise z Int
70-
watchPosition =
71-
Native.Geolocation.watchPosition
7288

89+
{-| Subscribe to changes in the device's position. You provide two callbacks.
90+
One for when a location has been successfully reported, and another for when
91+
there has been some sort of problem.
7392
74-
clearWatch : Int -> Promise x ()
75-
clearWatch =
76-
Native.Geolocation.clearWatch
93+
When you run the task to create a subscription, you will get back an integer
94+
that uniquely identifies this subscription. You can give that identifier to
95+
`unsubscribe` to stop getting updates.
96+
-}
97+
subscribe : Options -> (Location -> Task x a) -> (Error -> Task y b) -> Task z Int
98+
subscribe =
99+
Native.Geolocation.subscribe
77100

78101

102+
{-| Each subscription is uniquely identified by an integer. You can cancel a
103+
subscription by giving that integer to `unsubscribe`.
104+
-}
105+
unsubscribe : Int -> Task x ()
106+
unsubscribe =
107+
Native.Geolocation.unsubscribe
108+
109+
110+
{-| There are a couple options you can mess with when requesting location data.
111+
112+
* `enableHighAccuracy` — When enabled, the device will attempt to provide
113+
a more accurate location. This can result in slower response times or
114+
increased power consumption (with a GPS chip on a mobile device for example).
115+
When disabled, the device can take the liberty to save resources by responding
116+
more quickly and/or using less power.
117+
* `timeout` — Requesting a location can take time, so you have the option
118+
to provide an upper bound in milliseconds on that wait.
119+
* `maximumAge` — This API can return cached locations. If this is set
120+
to `Just 400` you may get cached locations as long as they were read in the
121+
last 400 milliseconds. If this is `Nothing` then the device must attempt
122+
to retrieve the current location every time.
123+
-}
79124
type alias Options =
80125
{ enableHighAccuracy : Bool
81126
, timeout : Maybe Int
82127
, maximumAge : Maybe Int
83128
}
84129

85130

131+
{-| The options you will want in 99% of cases. This will get you faster
132+
results, less battery drain, no surprise failures due to timeouts, and no
133+
surprising cached results.
134+
135+
{ enableHighAccuracy = False
136+
, timeout = Nothing
137+
, maximumAge = Nothing
138+
}
139+
-}
86140
defaultOptions : Options
87141
defaultOptions =
88142
{ enableHighAccuracy = False
89143
, timeout = Nothing
90144
, maximumAge = Nothing
91-
}
145+
}

src/Native/Geolocation.js

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,94 +9,95 @@ Elm.Native.Geolocation.make = function(localRuntime) {
99
}
1010

1111
var Maybe = Elm.Maybe.make(localRuntime);
12-
var Promise = Elm.Native.Promise.make(localRuntime);
12+
var Task = Elm.Native.Task.make(localRuntime);
1313

1414

1515
// JS values to Elm values
1616

17-
function maybe(value) {
18-
return value === null
19-
? Maybe.Nothing
20-
: Maybe.Just(value);
17+
function maybe(value)
18+
{
19+
return value === null ? Maybe.Nothing : Maybe.Just(value);
2120
}
2221

23-
function elmPosition(rawPosition) {
22+
function elmPosition(rawPosition)
23+
{
2424
var coords = rawPosition.coords;
2525
return {
26-
_: {},
27-
timestamp: rawPosition.timestamp,
28-
coords: {
29-
_: {},
30-
latitude: coords.latitude,
31-
longitude: coords.longitude,
32-
altitude: maybe(coords.altitude),
33-
accuracy: coords.accuracy,
34-
altitudeAccuracy: maybe(coords.altitudeAccuracy),
35-
heading: maybe(coords.heading),
36-
speed: maybe(coords.speed)
37-
}
26+
latitude: coords.latitude,
27+
longitude: coords.longitude,
28+
altitude: maybe(coords.altitude),
29+
accuracy: coords.accuracy,
30+
altitudeAccuracy: maybe(coords.altitudeAccuracy),
31+
heading: maybe(coords.heading),
32+
speed: maybe(coords.speed),
33+
timestamp: rawPosition.timestamp
3834
};
3935
}
4036

4137
var errorTypes = ['PermissionDenied', 'PositionUnavailable', 'Timeout'];
4238

43-
function elmError(rawError) {
39+
function elmError(rawError)
40+
{
4441
return {
4542
ctor: errorTypes[rawError.code - 1],
4643
_0: rawError.message
4744
};
4845
}
4946

50-
function jsOptions(options) {
47+
function jsOptions(options)
48+
{
5149
return {
5250
enableHighAccuracy: options.enableHighAccuracy,
5351
timeout: options.timeout._0 || Infinity,
54-
maximumAge: options.maximumAge._0
52+
maximumAge: options.maximumAge._0 || 0
5553
};
5654
}
5755

5856

5957
// actually do geolocation stuff
6058

61-
function currentPosition(options) {
62-
return Promise.asyncFunction(function(callback) {
63-
function onSuccess(rawPosition) {
64-
callback(Promise.succeed(elmPosition(rawPosition)));
59+
function current(options)
60+
{
61+
return Task.asyncFunction(function(callback) {
62+
function onSuccess(rawPosition)
63+
{
64+
callback(Task.succeed(elmPosition(rawPosition)));
6565
}
66-
function onError(rawError) {
67-
callback(Promise.fail(elmError(rawError)));
66+
function onError(rawError)
67+
{
68+
callback(Task.fail(elmError(rawError)));
6869
}
69-
navigator.geolocation.getCurrentPosition(
70-
onSuccess, onError, jsOptions(options)
71-
);
70+
navigator.geolocation.getCurrentPosition(onSuccess, onError, jsOptions(options));
7271
});
7372
}
7473

75-
function watchPosition(options, successPromise, errorPromise) {
76-
return Promise.asyncFunction(function(callback) {
77-
function onSuccess(rawPosition) {
78-
Promise.spawn(successPromise(elmPosition(rawPosition)));
74+
function subscribe(options, successTask, errorTask)
75+
{
76+
return Task.asyncFunction(function(callback) {
77+
function onSuccess(rawPosition)
78+
{
79+
Task.spawn(successTask(elmPosition(rawPosition)));
7980
}
80-
function onError(rawError) {
81-
Promise.spawn(errorPromise(elmError(rawError)));
81+
function onError(rawError)
82+
{
83+
Task.spawn(errorTask(elmError(rawError)));
8284
}
83-
var id = navigator.geolocation.watchPosition(
84-
onSuccess, onError, jsOptions(options)
85-
);
86-
callback(Promise.succeed(id));
85+
var id = navigator.geolocation.watchPosition(onSuccess, onError, jsOptions(options));
86+
callback(Task.succeed(id));
8787
});
8888
}
8989

90-
function clearWatch(id) {
91-
return Promise.asyncFunction(function(callback) {
90+
function unsubscribe(id)
91+
{
92+
return Task.asyncFunction(function(callback) {
9293
navigator.geolocation.clearWatch(id);
93-
callback(Promise.succeed(Utils.Tuple0));
94+
callback(Task.succeed(Utils.Tuple0));
9495
});
9596
}
9697

9798
return localRuntime.Native.Geolocation.values = {
98-
currentPosition: currentPosition,
99-
watchPosition: F3(watchPosition),
100-
clearWatch: clearWatch
99+
current: current,
100+
subscribe: F3(subscribe),
101+
unsubscribe: unsubscribe
101102
};
102103
};

0 commit comments

Comments
 (0)