Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

navigate to coordinates endpoint #408

Closed
wants to merge 9 commits into from
9 changes: 7 additions & 2 deletions src/actions/history.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LOCATION_CHANGE } from 'connected-react-router';
import { getDongleID, getZoom, getPrimeNav, getClipsNav } from '../url';
import { primeNav, selectDevice, pushTimelineRange } from './index';
import { getDongleID, getZoom, getPrimeNav, getClipsNav, getNavigationNav } from '../url';
import { primeNav, selectDevice, pushTimelineRange, navigateToDestination } from './index';
import { clipsExit, fetchClipsDetails, fetchClipsList } from './clips';

export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action) => {
Expand Down Expand Up @@ -28,6 +28,11 @@ export const onHistoryMiddleware = ({ dispatch, getState }) => (next) => (action
dispatch(primeNav(pathPrimeNav));
}

const pathNavigationNav = getNavigationNav(action.payload.location.pathname, action.payload.location.search);
if (pathNavigationNav && pathNavigationNav != state.navigationNav) {
dispatch(navigateToDestination(pathNavigationNav));
}

const pathClipsNav = getClipsNav(action.payload.location.pathname);
if (pathClipsNav === null && state.clips) {
dispatch(clipsExit());
Expand Down
35 changes: 35 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { resetPlayback, selectLoop } from '../timeline/playback';
import { getSegmentFetchRange, hasRoutesData } from '../timeline/segments';
import { getClipsNav } from '../url';
import { getDeviceFromState, deviceVersionAtLeast } from '../utils';
import { coordinatesFromMapsUrl } from '../utils/maps';

let routesRequest = null;

Expand Down Expand Up @@ -150,6 +151,40 @@ export function primeNav(nav, allowPathChange = true) {
};
}

export function navigateToDestination(destination) {
return (dispatch, getState) => {
const state = getState();
if (!state.dongleId) {
return;
}

console.log(destination);
let {url, lat, long} = destination;
var latLongPromise = null;
if (url) {
latLongPromise = coordinatesFromMapsUrl(url);
} else if (lat && long) {
latLongPromise = Promise.resolve([lat, long]);
} else {
return;
}

latLongPromise.then((coords) => {
let destination = {
latitude: coords[0],
longitude: coords[1],
};

if (state.navigationDestination !== destination) {
dispatch({
type: Types.ACTION_SET_NAVIGATION_DESTINATION,
navigationDestination: destination,
});
}
});
};
}

export function fetchSharedDevice(dongleId) {
return async (dispatch) => {
try {
Expand Down
3 changes: 3 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const ACTION_PRIME_NAV = 'ACTION_PRIME_NAV';
export const ACTION_PRIME_SUBSCRIPTION = 'ACTION_PRIME_SUBSCRIPTION';
export const ACTION_PRIME_SUBSCRIBE_INFO = 'ACTION_PRIME_SUBSCRIBE_INFO';

// map
export const ACTION_SET_NAVIGATION_DESTINATION = 'ACTION_SET_NAVIGATION_DESTINATION';

// playback
export const ACTION_SEEK = 'action_seek';
export const ACTION_PAUSE = 'action_pause';
Expand Down
5 changes: 3 additions & 2 deletions src/components/Dashboard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const DashboardLoading = () => (
</Grid>
);

const Dashboard = ({ primeNav, device, dongleId }) => {
const Dashboard = ({ primeNav, device, dongleId, navigationDestination }) => {
if (!device || !dongleId) {
return <DashboardLoading />;
}
Expand All @@ -30,7 +30,7 @@ const Dashboard = ({ primeNav, device, dongleId }) => {
? <Prime />
: (
<>
<Navigation hasNav={device.prime && device.eligible_features?.nav} />
<Navigation hasNav={device.prime && device.eligible_features?.nav} navigationDestination={navigationDestination} />
<DeviceInfo />
<DriveList />
</>
Expand All @@ -44,6 +44,7 @@ const stateToProps = Obstruction({
dongleId: 'dongleId',
primeNav: 'primeNav',
device: 'device',
navigationDestination: 'navigationDestination',
});

export default connect(stateToProps)(Dashboard);
42 changes: 41 additions & 1 deletion src/components/Navigation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ class Navigation extends Component {
}

componentDidUpdate(prevProps, prevState) {
const { dongleId, device } = this.props;
const { dongleId, device, navigationDestination } = this.props;
const { geoLocateCoords, search, carLastLocation, carNetworkLocation, searchSelect, favoriteLocations } = this.state;

if ((carLastLocation && !prevState.carLastLocation) || (carNetworkLocation && !prevState.carNetworkLocation)
Expand All @@ -354,6 +354,12 @@ class Navigation extends Component {
this.flyToMarkers();
}

if (prevProps.navigationDestination !== navigationDestination) {
this.getDeviceLastLocation().then(() => {
this.setDestination(navigationDestination.latitude, navigationDestination.longitude);
})
}

if (prevProps.dongleId !== dongleId) {
this.setState({
...initialState,
Expand Down Expand Up @@ -394,6 +400,40 @@ class Navigation extends Component {
}
}

setDestination(latitude, longitude) {
this.focus();

const item = {
favoriteId: null,
favoriteIcon: null,
address: {
label: '',
},
title: '',
resultType: 'place',
position: {
lat: latitude,
lng: longitude,
},
};
this.onSearchSelect(item, 'pin');
reverseLookup([longitude, latitude], false).then((location) => {
if (!location) {
return;
}

this.setState((prevState) => ({
searchSelect: {
...prevState.searchSelect,
address: {
label: location.details,
},
title: location.place,
},
}));
});
}

updateDevice() {
if (Demo.isDemo()) {
return;
Expand Down
3 changes: 3 additions & 0 deletions src/reducers/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ export default function reducer(_state, action) {
end: action.end,
};
break;
case Types.ACTION_SET_NAVIGATION_DESTINATION:
state.navigationDestination = action.navigationDestination
break;
default:
return state;
}
Expand Down
21 changes: 21 additions & 0 deletions src/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,24 @@ export function getClipsNav(pathname) {
}
return null;
}

export function getNavigationNav(pathname, search) {
let parts = pathname.split('/');
parts = parts.filter((m) => m.length);

if (parts.length >= 2 && parts[0] !== 'auth' && parts[1] == 'navigate') {
let params = new URLSearchParams(search);
if (params.has('url')) {
return {
url: params.get('url'),
}
} else if (params.has('lat') && params.has('long')) {
return {
lat: parseFloat(params.get('lat')),
long: parseFloat(params.get('long')),
};
}
return {};
}
return null;
}
29 changes: 29 additions & 0 deletions src/utils/maps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { forwardLookup } from "./geocode";

export async function coordinatesFromMapsUrl(urlString) {
let url = new URL(urlString);
if (url.hostname.endsWith('maps.apple.com')) {
if (!url.searchParams.has('ll')) {
return null;
}

let ll = url.searchParams.get('ll');
let coords = ll.split(',').map((x) => parseFloat(x));
return coords;
} else if (['maps.google.com', 'google.com'].find((h) => url.hostname.endsWith(h))) {
if (url.searchParams.has('q')) { // maps.google.com?q=...
let query = url.searchParams.get('q');
let places = await forwardLookup(query, null, null);
if (places.length === 0) {
return null;
}

let position = places[0].position;
return position ? [position.lat, position.lng] : null;
} else {
return null;
}
}

return null;
}
21 changes: 21 additions & 0 deletions src/utils/maps.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-env jest */
import { coordinatesFromMapsUrl } from './maps';
import * as geocode from './geocode';

jest.mock('./geocode', () => ({
forwardLookup: jest.fn()
}));

describe('url parsing', () => {
it('should parse coordinates apple maps url', async () => {
let coords = await coordinatesFromMapsUrl('https://maps.apple.com/?ll=37.3319,-122.0312');
expect(coords).toEqual([37.3319, -122.0312]);
});

it('should reverse lookup coordinates based of query of maps.google.com?q=', async () => {
geocode.forwardLookup.mockResolvedValue([{position: {lat: 37.3319, lng: -122.0312}}]);

let coords = await coordinatesFromMapsUrl('https://www.google.com/maps?q=Some+place+somewhere');
expect(coords).toEqual([37.3319, -122.0312]);
});
});