Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit ae49084

Browse files
authored
Look up tile server info in homeserver's .well-known area (#7623)
1 parent 20819df commit ae49084

File tree

4 files changed

+142
-45
lines changed

4 files changed

+142
-45
lines changed

src/components/views/location/LocationPicker.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ import React, { SyntheticEvent } from 'react';
1818
import maplibregl from 'maplibre-gl';
1919
import { logger } from "matrix-js-sdk/src/logger";
2020
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
21+
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
2122

22-
import SdkConfig from '../../../SdkConfig';
2323
import DialogButtons from "../elements/DialogButtons";
2424
import { _t } from '../../../languageHandler';
2525
import { replaceableComponent } from "../../../utils/replaceableComponent";
2626
import MemberAvatar from '../avatars/MemberAvatar';
2727
import MatrixClientContext from '../../../contexts/MatrixClientContext';
2828
import Modal from '../../../Modal';
2929
import ErrorDialog from '../dialogs/ErrorDialog';
30+
import { findMapStyleUrl } from '../messages/MLocationBody';
31+
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
3032

3133
interface IProps {
3234
sender: RoomMember;
@@ -51,9 +53,9 @@ interface IState {
5153
class LocationPicker extends React.Component<IProps, IState> {
5254
public static contextType = MatrixClientContext;
5355
public context!: React.ContextType<typeof MatrixClientContext>;
54-
private map: maplibregl.Map;
55-
private geolocate: maplibregl.GeolocateControl;
56-
private marker: maplibregl.Marker;
56+
private map?: maplibregl.Map = null;
57+
private geolocate?: maplibregl.GeolocateControl = null;
58+
private marker?: maplibregl.Marker = null;
5759

5860
constructor(props: IProps) {
5961
super(props);
@@ -69,15 +71,16 @@ class LocationPicker extends React.Component<IProps, IState> {
6971
};
7072

7173
componentDidMount() {
72-
const config = SdkConfig.get();
73-
this.map = new maplibregl.Map({
74-
container: 'mx_LocationPicker_map',
75-
style: config.map_style_url,
76-
center: [0, 0],
77-
zoom: 1,
78-
});
74+
this.context.on("WellKnown.client", this.updateStyleUrl);
7975

8076
try {
77+
this.map = new maplibregl.Map({
78+
container: 'mx_LocationPicker_map',
79+
style: findMapStyleUrl(),
80+
center: [0, 0],
81+
zoom: 1,
82+
});
83+
8184
// Add geolocate control to the map.
8285
this.geolocate = new maplibregl.GeolocateControl({
8386
positionOptions: {
@@ -124,18 +127,26 @@ class LocationPicker extends React.Component<IProps, IState> {
124127

125128
this.geolocate.on('geolocate', this.onGeolocate);
126129
} catch (e) {
127-
logger.error("Failed to render map", e.error);
128-
this.setState({ error: e.error });
130+
logger.error("Failed to render map", e);
131+
this.setState({ error: e });
129132
}
130133
}
131134

132135
componentWillUnmount() {
133136
this.geolocate?.off('geolocate', this.onGeolocate);
137+
this.context.off("WellKnown.client", this.updateStyleUrl);
134138
}
135139

140+
private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
141+
const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
142+
if (style) {
143+
this.map?.setStyle(style);
144+
}
145+
};
146+
136147
private onGeolocate = (position: GeolocationPosition) => {
137148
this.setState({ position });
138-
this.marker.setLngLat(
149+
this.marker?.setLngLat(
139150
new maplibregl.LngLat(
140151
position.coords.longitude,
141152
position.coords.latitude,

src/components/views/location/LocationViewDialog.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ limitations under the License.
1616

1717
import React from 'react';
1818
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
19+
import { IClientWellKnown, MatrixClient } from 'matrix-js-sdk/src/client';
1920

2021
import { replaceableComponent } from "../../../utils/replaceableComponent";
2122
import BaseDialog from "../dialogs/BaseDialog";
2223
import { IDialogProps } from "../dialogs/IDialogProps";
2324
import { createMap, LocationBodyContent, locationEventGeoUri, parseGeoUri } from '../messages/MLocationBody';
25+
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
2426

2527
interface IProps extends IDialogProps {
28+
matrixClient: MatrixClient;
2629
mxEvent: MatrixEvent;
2730
}
2831

@@ -50,6 +53,8 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
5053
return;
5154
}
5255

56+
this.props.matrixClient.on("WellKnown.client", this.updateStyleUrl);
57+
5358
this.map = createMap(
5459
this.coords,
5560
true,
@@ -59,6 +64,17 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
5964
);
6065
}
6166

67+
componentWillUnmount() {
68+
this.props.matrixClient.off("WellKnown.client", this.updateStyleUrl);
69+
}
70+
71+
private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
72+
const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
73+
if (style) {
74+
this.map?.setStyle(style);
75+
}
76+
};
77+
6278
private getBodyId = () => {
6379
return `mx_LocationViewDialog_${this.props.mxEvent.getId()}`;
6480
};

src/components/views/messages/MLocationBody.tsx

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
ILocationContent,
2525
LOCATION_EVENT_TYPE,
2626
} from 'matrix-js-sdk/src/@types/location';
27+
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
2728

2829
import SdkConfig from '../../../SdkConfig';
2930
import { replaceableComponent } from "../../../utils/replaceableComponent";
@@ -35,16 +36,21 @@ import LocationViewDialog from '../location/LocationViewDialog';
3536
import TooltipTarget from '../elements/TooltipTarget';
3637
import { Alignment } from '../elements/Tooltip';
3738
import AccessibleButton from '../elements/AccessibleButton';
39+
import { getTileServerWellKnown, tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
40+
import MatrixClientContext from '../../../contexts/MatrixClientContext';
3841

3942
interface IState {
4043
error: Error;
4144
}
4245

4346
@replaceableComponent("views.messages.MLocationBody")
4447
export default class MLocationBody extends React.Component<IBodyProps, IState> {
48+
public static contextType = MatrixClientContext;
49+
public context!: React.ContextType<typeof MatrixClientContext>;
4550
private coords: GeolocationCoordinates;
4651
private bodyId: string;
4752
private markerId: string;
53+
private map?: maplibregl.Map = null;
4854

4955
constructor(props: IBodyProps) {
5056
super(props);
@@ -65,7 +71,9 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
6571
return;
6672
}
6773

68-
createMap(
74+
this.context.on("WellKnown.client", this.updateStyleUrl);
75+
76+
this.map = createMap(
6977
this.coords,
7078
false,
7179
this.bodyId,
@@ -74,6 +82,17 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
7482
);
7583
}
7684

85+
componentWillUnmount() {
86+
this.context.off("WellKnown.client", this.updateStyleUrl);
87+
}
88+
89+
private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
90+
const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
91+
if (style) {
92+
this.map?.setStyle(style);
93+
}
94+
};
95+
7796
private onClick = (
7897
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
7998
) => {
@@ -87,7 +106,10 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
87106
'Location View',
88107
'',
89108
LocationViewDialog,
90-
{ mxEvent: this.props.mxEvent },
109+
{
110+
matrixClient: this.context,
111+
mxEvent: this.props.mxEvent,
112+
},
91113
"mx_LocationViewDialog_wrapper",
92114
false, // isPriority
93115
true, // isStatic
@@ -206,42 +228,68 @@ function ZoomButtons(props: IZoomButtonsProps): React.ReactElement<HTMLDivElemen
206228
</div>;
207229
}
208230

231+
/**
232+
* Look up what map tile server style URL was provided in the homeserver's
233+
* .well-known location, or, failing that, in our local config, or, failing
234+
* that, defaults to the same tile server listed by matrix.org.
235+
*/
236+
export function findMapStyleUrl(): string {
237+
const mapStyleUrl = (
238+
getTileServerWellKnown()?.map_style_url ??
239+
SdkConfig.get().map_style_url
240+
);
241+
242+
if (!mapStyleUrl) {
243+
throw new Error(
244+
"'map_style_url' missing from homeserver .well-known area, and " +
245+
"missing from from config.json.",
246+
);
247+
}
248+
249+
return mapStyleUrl;
250+
}
251+
209252
export function createMap(
210253
coords: GeolocationCoordinates,
211254
interactive: boolean,
212255
bodyId: string,
213256
markerId: string,
214257
onError: (error: Error) => void,
215258
): maplibregl.Map {
216-
const styleUrl = SdkConfig.get().map_style_url;
217-
const coordinates = new maplibregl.LngLat(coords.longitude, coords.latitude);
218-
219-
const map = new maplibregl.Map({
220-
container: bodyId,
221-
style: styleUrl,
222-
center: coordinates,
223-
zoom: 15,
224-
interactive,
225-
});
226-
227-
new maplibregl.Marker({
228-
element: document.getElementById(markerId),
229-
anchor: 'bottom',
230-
offset: [0, -1],
231-
})
232-
.setLngLat(coordinates)
233-
.addTo(map);
234-
235-
map.on('error', (e) => {
236-
logger.error(
237-
"Failed to load map: check map_style_url in config.json has a "
238-
+ "valid URL and API key",
239-
e.error,
240-
);
241-
onError(e.error);
242-
});
243-
244-
return map;
259+
try {
260+
const styleUrl = findMapStyleUrl();
261+
const coordinates = new maplibregl.LngLat(coords.longitude, coords.latitude);
262+
263+
const map = new maplibregl.Map({
264+
container: bodyId,
265+
style: styleUrl,
266+
center: coordinates,
267+
zoom: 15,
268+
interactive,
269+
});
270+
271+
new maplibregl.Marker({
272+
element: document.getElementById(markerId),
273+
anchor: 'bottom',
274+
offset: [0, -1],
275+
})
276+
.setLngLat(coordinates)
277+
.addTo(map);
278+
279+
map.on('error', (e) => {
280+
logger.error(
281+
"Failed to load map: check map_style_url in config.json has a "
282+
+ "valid URL and API key",
283+
e.error,
284+
);
285+
onError(e.error);
286+
});
287+
288+
return map;
289+
} catch (e) {
290+
logger.error("Failed to render map", e);
291+
onError(e);
292+
}
245293
}
246294

247295
/**

src/utils/WellKnownUtils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
18+
import { UnstableValue } from 'matrix-js-sdk/src/NamespacedValue';
19+
1720
import { MatrixClientPeg } from '../MatrixClientPeg';
1821

1922
const CALL_BEHAVIOUR_WK_KEY = "io.element.call_behaviour";
2023
const E2EE_WK_KEY = "io.element.e2ee";
2124
const E2EE_WK_KEY_DEPRECATED = "im.vector.riot.e2ee";
25+
const TILE_SERVER_WK_KEY = new UnstableValue(
26+
"m.tile_server", "org.matrix.msc3488.tile_server");
2227

2328
/* eslint-disable camelcase */
2429
export interface ICallBehaviourWellKnown {
@@ -30,6 +35,10 @@ export interface IE2EEWellKnown {
3035
secure_backup_required?: boolean;
3136
secure_backup_setup_methods?: SecureBackupSetupMethod[];
3237
}
38+
39+
export interface ITileServerWellKnown {
40+
map_style_url?: string;
41+
}
3342
/* eslint-enable camelcase */
3443

3544
export function getCallBehaviourWellKnown(): ICallBehaviourWellKnown {
@@ -48,6 +57,19 @@ export function getE2EEWellKnown(): IE2EEWellKnown {
4857
return null;
4958
}
5059

60+
export function getTileServerWellKnown(): ITileServerWellKnown | undefined {
61+
return tileServerFromWellKnown(MatrixClientPeg.get().getClientWellKnown());
62+
}
63+
64+
export function tileServerFromWellKnown(
65+
clientWellKnown?: IClientWellKnown | undefined,
66+
): ITileServerWellKnown {
67+
return (
68+
clientWellKnown?.[TILE_SERVER_WK_KEY.name] ??
69+
clientWellKnown?.[TILE_SERVER_WK_KEY.altName]
70+
);
71+
}
72+
5173
export function isSecureBackupRequired(): boolean {
5274
const wellKnown = getE2EEWellKnown();
5375
return wellKnown && wellKnown["secure_backup_required"] === true;

0 commit comments

Comments
 (0)