Extract location utils from components (#8152)
* extract util functions from MLocationBody Signed-off-by: Kerry Archibald <kerrya@element.io> * disassemble mlocationbody Signed-off-by: Kerry Archibald <kerrya@element.io> * tidy and add copyrights Signed-off-by: Kerry Archibald <kerrya@element.io> * move types and utils from components/location to utils Signed-off-by: Kerry Archibald <kerrya@element.io> * i18n Signed-off-by: Kerry Archibald <kerrya@element.io> * empty line Signed-off-by: Kerry Archibald <kerrya@element.io>watcha-feature/make-nextcloud-documents-integration-a-local-widget
parent
0d513b3a2d
commit
1397652f52
@ -0,0 +1,21 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
export * from './findMapStyleUrl'; |
||||
export * from './locationEventGeoUri'; |
||||
export * from './LocationShareErrors'; |
||||
export * from './map'; |
||||
export * from './parseGeoUri'; |
||||
@ -0,0 +1,31 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix"; |
||||
import { M_LOCATION } from "matrix-js-sdk/src/@types/location"; |
||||
|
||||
/** |
||||
* Find the geo-URI contained within a location event. |
||||
*/ |
||||
export const locationEventGeoUri = (mxEvent: MatrixEvent): string => { |
||||
// unfortunately we're stuck supporting legacy `content.geo_uri`
|
||||
// events until the end of days, or until we figure out mutable
|
||||
// events - so folks can read their old chat history correctly.
|
||||
// https://github.com/matrix-org/matrix-doc/issues/3516
|
||||
const content = mxEvent.getContent(); |
||||
const loc = M_LOCATION.findIn(content) as { uri?: string }; |
||||
return loc ? loc.uri : content['geo_uri']; |
||||
}; |
||||
@ -0,0 +1,93 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import maplibregl from "maplibre-gl"; |
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix"; |
||||
import { M_LOCATION } from "matrix-js-sdk/src/@types/location"; |
||||
import { logger } from "matrix-js-sdk/src/logger"; |
||||
|
||||
import { parseGeoUri } from "./parseGeoUri"; |
||||
import { findMapStyleUrl } from "./findMapStyleUrl"; |
||||
import { LocationShareError } from "./LocationShareErrors"; |
||||
|
||||
export const createMap = ( |
||||
coords: GeolocationCoordinates, |
||||
interactive: boolean, |
||||
bodyId: string, |
||||
markerId: string, |
||||
onError: (error: Error) => void, |
||||
): maplibregl.Map => { |
||||
try { |
||||
const styleUrl = findMapStyleUrl(); |
||||
const coordinates = new maplibregl.LngLat(coords.longitude, coords.latitude); |
||||
|
||||
const map = new maplibregl.Map({ |
||||
container: bodyId, |
||||
style: styleUrl, |
||||
center: coordinates, |
||||
zoom: 15, |
||||
interactive, |
||||
}); |
||||
|
||||
new maplibregl.Marker({ |
||||
element: document.getElementById(markerId), |
||||
anchor: 'bottom', |
||||
offset: [0, -1], |
||||
}) |
||||
.setLngLat(coordinates) |
||||
.addTo(map); |
||||
|
||||
map.on('error', (e) => { |
||||
logger.error( |
||||
"Failed to load map: check map_style_url in config.json has a " |
||||
+ "valid URL and API key", |
||||
e.error, |
||||
); |
||||
onError(new Error(LocationShareError.MapStyleUrlNotReachable)); |
||||
}); |
||||
|
||||
return map; |
||||
} catch (e) { |
||||
logger.error("Failed to render map", e); |
||||
onError(e); |
||||
} |
||||
}; |
||||
|
||||
const makeLink = (coords: GeolocationCoordinates): string => { |
||||
return ( |
||||
"https://www.openstreetmap.org/" + |
||||
`?mlat=${coords.latitude}` + |
||||
`&mlon=${coords.longitude}` + |
||||
`#map=16/${coords.latitude}/${coords.longitude}` |
||||
); |
||||
}; |
||||
|
||||
export const createMapSiteLink = (event: MatrixEvent): string => { |
||||
const content: Object = event.getContent(); |
||||
const mLocation = content[M_LOCATION.name]; |
||||
if (mLocation !== undefined) { |
||||
const uri = mLocation["uri"]; |
||||
if (uri !== undefined) { |
||||
return makeLink(parseGeoUri(uri)); |
||||
} |
||||
} else { |
||||
const geoUri = content["geo_uri"]; |
||||
if (geoUri) { |
||||
return makeLink(parseGeoUri(geoUri)); |
||||
} |
||||
} |
||||
return null; |
||||
}; |
||||
@ -0,0 +1,45 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
export const parseGeoUri = (uri: string): GeolocationCoordinates => { |
||||
function parse(s: string): number { |
||||
const ret = parseFloat(s); |
||||
if (Number.isNaN(ret)) { |
||||
return undefined; |
||||
} else { |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
const m = uri.match(/^\s*geo:(.*?)\s*$/); |
||||
if (!m) return; |
||||
const parts = m[1].split(';'); |
||||
const coords = parts[0].split(','); |
||||
let uncertainty: number; |
||||
for (const param of parts.slice(1)) { |
||||
const m = param.match(/u=(.*)/); |
||||
if (m) uncertainty = parse(m[1]); |
||||
} |
||||
return { |
||||
latitude: parse(coords[0]), |
||||
longitude: parse(coords[1]), |
||||
altitude: parse(coords[2]), |
||||
accuracy: uncertainty, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}; |
||||
}; |
||||
@ -0,0 +1,50 @@ |
||||
/* |
||||
Copyright 2021 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import { LocationAssetType, M_LOCATION } from "matrix-js-sdk/src/@types/location"; |
||||
import { makeLocationContent } from "matrix-js-sdk/src/content-helpers"; |
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix"; |
||||
|
||||
let id = 1; |
||||
export const makeLegacyLocationEvent = (geoUri: string): MatrixEvent => { |
||||
return new MatrixEvent( |
||||
{ |
||||
"event_id": `$${++id}`, |
||||
"type": M_LOCATION.name, |
||||
"content": { |
||||
"body": "Something about where I am", |
||||
"msgtype": "m.location", |
||||
"geo_uri": geoUri, |
||||
}, |
||||
}, |
||||
); |
||||
}; |
||||
|
||||
export const makeLocationEvent = (geoUri: string, assetType?: LocationAssetType): MatrixEvent => { |
||||
return new MatrixEvent( |
||||
{ |
||||
"event_id": `$${++id}`, |
||||
"type": M_LOCATION.name, |
||||
"content": makeLocationContent( |
||||
`Found at ${geoUri} at 2021-12-21T12:22+0000`, |
||||
geoUri, |
||||
252523, |
||||
"Human-readable label", |
||||
assetType, |
||||
), |
||||
}, |
||||
); |
||||
}; |
||||
@ -0,0 +1,28 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import { locationEventGeoUri } from "../../../src/utils/location"; |
||||
import { makeLegacyLocationEvent, makeLocationEvent } from "../../test-utils/location"; |
||||
|
||||
describe('locationEventGeoUri()', () => { |
||||
it('returns m.location uri when available', () => { |
||||
expect(locationEventGeoUri(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual("geo:51.5076,-0.1276"); |
||||
}); |
||||
|
||||
it('returns legacy uri when m.location content not found', () => { |
||||
expect(locationEventGeoUri(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual("geo:51.5076,-0.1276"); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,47 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import { createMapSiteLink } from "../../../src/utils/location"; |
||||
import { mkMessage } from "../../test-utils"; |
||||
import { makeLegacyLocationEvent, makeLocationEvent } from "../../test-utils/location"; |
||||
|
||||
describe("createMapSiteLink", () => { |
||||
it("returns null if event does not contain geouri", () => { |
||||
expect(createMapSiteLink(mkMessage({ |
||||
room: '1', user: '@sender:server', event: true, |
||||
}))).toBeNull(); |
||||
}); |
||||
|
||||
it("returns OpenStreetMap link if event contains m.location", () => { |
||||
expect( |
||||
createMapSiteLink(makeLocationEvent("geo:51.5076,-0.1276")), |
||||
).toEqual( |
||||
"https://www.openstreetmap.org/" + |
||||
"?mlat=51.5076&mlon=-0.1276" + |
||||
"#map=16/51.5076/-0.1276", |
||||
); |
||||
}); |
||||
|
||||
it("returns OpenStreetMap link if event contains geo_uri", () => { |
||||
expect( |
||||
createMapSiteLink(makeLegacyLocationEvent("geo:51.5076,-0.1276")), |
||||
).toEqual( |
||||
"https://www.openstreetmap.org/" + |
||||
"?mlat=51.5076&mlon=-0.1276" + |
||||
"#map=16/51.5076/-0.1276", |
||||
); |
||||
}); |
||||
}); |
||||
@ -0,0 +1,157 @@ |
||||
/* |
||||
Copyright 2022 The Matrix.org Foundation C.I.C. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
*/ |
||||
|
||||
import { parseGeoUri } from "../../../src/utils/location/parseGeoUri"; |
||||
|
||||
describe("parseGeoUri", () => { |
||||
it("fails if the supplied URI is empty", () => { |
||||
expect(parseGeoUri("")).toBeFalsy(); |
||||
}); |
||||
|
||||
// We use some examples from the spec, but don't check semantics
|
||||
// like two textually-different URIs being equal, since we are
|
||||
// just a humble parser.
|
||||
|
||||
// Note: we do not understand geo URIs with percent-encoded coords
|
||||
// or accuracy. It is RECOMMENDED in the spec never to percent-encode
|
||||
// these, but it is permitted, and we will fail to parse in that case.
|
||||
|
||||
it("rfc5870 6.1 Simple 3-dimensional", () => { |
||||
expect(parseGeoUri("geo:48.2010,16.3695,183")).toEqual( |
||||
{ |
||||
latitude: 48.2010, |
||||
longitude: 16.3695, |
||||
altitude: 183, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.2 Explicit CRS and accuracy", () => { |
||||
expect(parseGeoUri("geo:48.198634,16.371648;crs=wgs84;u=40")).toEqual( |
||||
{ |
||||
latitude: 48.198634, |
||||
longitude: 16.371648, |
||||
altitude: undefined, |
||||
accuracy: 40, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.4 Negative longitude and explicit CRS", () => { |
||||
expect(parseGeoUri("geo:90,-22.43;crs=WGS84")).toEqual( |
||||
{ |
||||
latitude: 90, |
||||
longitude: -22.43, |
||||
altitude: undefined, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.4 Integer lat and lon", () => { |
||||
expect(parseGeoUri("geo:90,46")).toEqual( |
||||
{ |
||||
latitude: 90, |
||||
longitude: 46, |
||||
altitude: undefined, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.4 Percent-encoded param value", () => { |
||||
expect(parseGeoUri("geo:66,30;u=6.500;FOo=this%2dthat")).toEqual( |
||||
{ |
||||
latitude: 66, |
||||
longitude: 30, |
||||
altitude: undefined, |
||||
accuracy: 6.500, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.4 Unknown param", () => { |
||||
expect(parseGeoUri("geo:66.0,30;u=6.5;foo=this-that>")).toEqual( |
||||
{ |
||||
latitude: 66.0, |
||||
longitude: 30, |
||||
altitude: undefined, |
||||
accuracy: 6.5, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("rfc5870 6.4 Multiple unknown params", () => { |
||||
expect(parseGeoUri("geo:70,20;foo=1.00;bar=white")).toEqual( |
||||
{ |
||||
latitude: 70, |
||||
longitude: 20, |
||||
altitude: undefined, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("Negative latitude", () => { |
||||
expect(parseGeoUri("geo:-7.5,20")).toEqual( |
||||
{ |
||||
latitude: -7.5, |
||||
longitude: 20, |
||||
altitude: undefined, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
|
||||
it("Zero altitude is not unknown", () => { |
||||
expect(parseGeoUri("geo:-7.5,-20,0")).toEqual( |
||||
{ |
||||
latitude: -7.5, |
||||
longitude: -20, |
||||
altitude: 0, |
||||
accuracy: undefined, |
||||
altitudeAccuracy: undefined, |
||||
heading: undefined, |
||||
speed: undefined, |
||||
}, |
||||
); |
||||
}); |
||||
}); |
||||
Loading…
Reference in new issue