You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-04 11:51:45 +03:00 
			
		
		
		
	Look up tile server info in homeserver's .well-known area (#7623)
This commit is contained in:
		@@ -18,8 +18,8 @@ import React, { SyntheticEvent } from 'react';
 | 
			
		||||
import maplibregl from 'maplibre-gl';
 | 
			
		||||
import { logger } from "matrix-js-sdk/src/logger";
 | 
			
		||||
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
 | 
			
		||||
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
 | 
			
		||||
 | 
			
		||||
import SdkConfig from '../../../SdkConfig';
 | 
			
		||||
import DialogButtons from "../elements/DialogButtons";
 | 
			
		||||
import { _t } from '../../../languageHandler';
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
@@ -27,6 +27,8 @@ import MemberAvatar from '../avatars/MemberAvatar';
 | 
			
		||||
import MatrixClientContext from '../../../contexts/MatrixClientContext';
 | 
			
		||||
import Modal from '../../../Modal';
 | 
			
		||||
import ErrorDialog from '../dialogs/ErrorDialog';
 | 
			
		||||
import { findMapStyleUrl } from '../messages/MLocationBody';
 | 
			
		||||
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    sender: RoomMember;
 | 
			
		||||
@@ -51,9 +53,9 @@ interface IState {
 | 
			
		||||
class LocationPicker extends React.Component<IProps, IState> {
 | 
			
		||||
    public static contextType = MatrixClientContext;
 | 
			
		||||
    public context!: React.ContextType<typeof MatrixClientContext>;
 | 
			
		||||
    private map: maplibregl.Map;
 | 
			
		||||
    private geolocate: maplibregl.GeolocateControl;
 | 
			
		||||
    private marker: maplibregl.Marker;
 | 
			
		||||
    private map?: maplibregl.Map = null;
 | 
			
		||||
    private geolocate?: maplibregl.GeolocateControl = null;
 | 
			
		||||
    private marker?: maplibregl.Marker = null;
 | 
			
		||||
 | 
			
		||||
    constructor(props: IProps) {
 | 
			
		||||
        super(props);
 | 
			
		||||
@@ -69,15 +71,16 @@ class LocationPicker extends React.Component<IProps, IState> {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    componentDidMount() {
 | 
			
		||||
        const config = SdkConfig.get();
 | 
			
		||||
        this.map = new maplibregl.Map({
 | 
			
		||||
            container: 'mx_LocationPicker_map',
 | 
			
		||||
            style: config.map_style_url,
 | 
			
		||||
            center: [0, 0],
 | 
			
		||||
            zoom: 1,
 | 
			
		||||
        });
 | 
			
		||||
        this.context.on("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.map = new maplibregl.Map({
 | 
			
		||||
                container: 'mx_LocationPicker_map',
 | 
			
		||||
                style: findMapStyleUrl(),
 | 
			
		||||
                center: [0, 0],
 | 
			
		||||
                zoom: 1,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Add geolocate control to the map.
 | 
			
		||||
            this.geolocate = new maplibregl.GeolocateControl({
 | 
			
		||||
                positionOptions: {
 | 
			
		||||
@@ -124,18 +127,26 @@ class LocationPicker extends React.Component<IProps, IState> {
 | 
			
		||||
 | 
			
		||||
            this.geolocate.on('geolocate', this.onGeolocate);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            logger.error("Failed to render map", e.error);
 | 
			
		||||
            this.setState({ error: e.error });
 | 
			
		||||
            logger.error("Failed to render map", e);
 | 
			
		||||
            this.setState({ error: e });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentWillUnmount() {
 | 
			
		||||
        this.geolocate?.off('geolocate', this.onGeolocate);
 | 
			
		||||
        this.context.off("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
 | 
			
		||||
        const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
 | 
			
		||||
        if (style) {
 | 
			
		||||
            this.map?.setStyle(style);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onGeolocate = (position: GeolocationPosition) => {
 | 
			
		||||
        this.setState({ position });
 | 
			
		||||
        this.marker.setLngLat(
 | 
			
		||||
        this.marker?.setLngLat(
 | 
			
		||||
            new maplibregl.LngLat(
 | 
			
		||||
                position.coords.longitude,
 | 
			
		||||
                position.coords.latitude,
 | 
			
		||||
 
 | 
			
		||||
@@ -16,13 +16,16 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
 | 
			
		||||
import { IClientWellKnown, MatrixClient } from 'matrix-js-sdk/src/client';
 | 
			
		||||
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
import BaseDialog from "../dialogs/BaseDialog";
 | 
			
		||||
import { IDialogProps } from "../dialogs/IDialogProps";
 | 
			
		||||
import { createMap, LocationBodyContent, locationEventGeoUri, parseGeoUri } from '../messages/MLocationBody';
 | 
			
		||||
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
 | 
			
		||||
 | 
			
		||||
interface IProps extends IDialogProps {
 | 
			
		||||
    matrixClient: MatrixClient;
 | 
			
		||||
    mxEvent: MatrixEvent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +53,8 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.props.matrixClient.on("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
 | 
			
		||||
        this.map = createMap(
 | 
			
		||||
            this.coords,
 | 
			
		||||
            true,
 | 
			
		||||
@@ -59,6 +64,17 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentWillUnmount() {
 | 
			
		||||
        this.props.matrixClient.off("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
 | 
			
		||||
        const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
 | 
			
		||||
        if (style) {
 | 
			
		||||
            this.map?.setStyle(style);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private getBodyId = () => {
 | 
			
		||||
        return `mx_LocationViewDialog_${this.props.mxEvent.getId()}`;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import {
 | 
			
		||||
    ILocationContent,
 | 
			
		||||
    LOCATION_EVENT_TYPE,
 | 
			
		||||
} from 'matrix-js-sdk/src/@types/location';
 | 
			
		||||
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
 | 
			
		||||
 | 
			
		||||
import SdkConfig from '../../../SdkConfig';
 | 
			
		||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
 | 
			
		||||
@@ -35,6 +36,8 @@ import LocationViewDialog from '../location/LocationViewDialog';
 | 
			
		||||
import TooltipTarget from '../elements/TooltipTarget';
 | 
			
		||||
import { Alignment } from '../elements/Tooltip';
 | 
			
		||||
import AccessibleButton from '../elements/AccessibleButton';
 | 
			
		||||
import { getTileServerWellKnown, tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
 | 
			
		||||
import MatrixClientContext from '../../../contexts/MatrixClientContext';
 | 
			
		||||
 | 
			
		||||
interface IState {
 | 
			
		||||
    error: Error;
 | 
			
		||||
@@ -42,9 +45,12 @@ interface IState {
 | 
			
		||||
 | 
			
		||||
@replaceableComponent("views.messages.MLocationBody")
 | 
			
		||||
export default class MLocationBody extends React.Component<IBodyProps, IState> {
 | 
			
		||||
    public static contextType = MatrixClientContext;
 | 
			
		||||
    public context!: React.ContextType<typeof MatrixClientContext>;
 | 
			
		||||
    private coords: GeolocationCoordinates;
 | 
			
		||||
    private bodyId: string;
 | 
			
		||||
    private markerId: string;
 | 
			
		||||
    private map?: maplibregl.Map = null;
 | 
			
		||||
 | 
			
		||||
    constructor(props: IBodyProps) {
 | 
			
		||||
        super(props);
 | 
			
		||||
@@ -65,7 +71,9 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        createMap(
 | 
			
		||||
        this.context.on("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
 | 
			
		||||
        this.map = createMap(
 | 
			
		||||
            this.coords,
 | 
			
		||||
            false,
 | 
			
		||||
            this.bodyId,
 | 
			
		||||
@@ -74,6 +82,17 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    componentWillUnmount() {
 | 
			
		||||
        this.context.off("WellKnown.client", this.updateStyleUrl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
 | 
			
		||||
        const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
 | 
			
		||||
        if (style) {
 | 
			
		||||
            this.map?.setStyle(style);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private onClick = (
 | 
			
		||||
        event: React.MouseEvent<HTMLDivElement, MouseEvent>,
 | 
			
		||||
    ) => {
 | 
			
		||||
@@ -87,7 +106,10 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
 | 
			
		||||
            'Location View',
 | 
			
		||||
            '',
 | 
			
		||||
            LocationViewDialog,
 | 
			
		||||
            { mxEvent: this.props.mxEvent },
 | 
			
		||||
            {
 | 
			
		||||
                matrixClient: this.context,
 | 
			
		||||
                mxEvent: this.props.mxEvent,
 | 
			
		||||
            },
 | 
			
		||||
            "mx_LocationViewDialog_wrapper",
 | 
			
		||||
            false, // isPriority
 | 
			
		||||
            true, // isStatic
 | 
			
		||||
@@ -206,6 +228,27 @@ function ZoomButtons(props: IZoomButtonsProps): React.ReactElement<HTMLDivElemen
 | 
			
		||||
    </div>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Look up what map tile server style URL was provided in the homeserver's
 | 
			
		||||
 * .well-known location, or, failing that, in our local config, or, failing
 | 
			
		||||
 * that, defaults to the same tile server listed by matrix.org.
 | 
			
		||||
 */
 | 
			
		||||
export function findMapStyleUrl(): string {
 | 
			
		||||
    const mapStyleUrl = (
 | 
			
		||||
        getTileServerWellKnown()?.map_style_url ??
 | 
			
		||||
        SdkConfig.get().map_style_url
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!mapStyleUrl) {
 | 
			
		||||
        throw new Error(
 | 
			
		||||
            "'map_style_url' missing from homeserver .well-known area, and " +
 | 
			
		||||
            "missing from from config.json.",
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mapStyleUrl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function createMap(
 | 
			
		||||
    coords: GeolocationCoordinates,
 | 
			
		||||
    interactive: boolean,
 | 
			
		||||
@@ -213,35 +256,40 @@ export function createMap(
 | 
			
		||||
    markerId: string,
 | 
			
		||||
    onError: (error: Error) => void,
 | 
			
		||||
): maplibregl.Map {
 | 
			
		||||
    const styleUrl = SdkConfig.get().map_style_url;
 | 
			
		||||
    const coordinates = new maplibregl.LngLat(coords.longitude, coords.latitude);
 | 
			
		||||
    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,
 | 
			
		||||
    });
 | 
			
		||||
        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);
 | 
			
		||||
        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(e.error);
 | 
			
		||||
    });
 | 
			
		||||
        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(e.error);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    return map;
 | 
			
		||||
        return map;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logger.error("Failed to render map", e);
 | 
			
		||||
        onError(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,16 @@ See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import { IClientWellKnown } from 'matrix-js-sdk/src/client';
 | 
			
		||||
import { UnstableValue } from 'matrix-js-sdk/src/NamespacedValue';
 | 
			
		||||
 | 
			
		||||
import { MatrixClientPeg } from '../MatrixClientPeg';
 | 
			
		||||
 | 
			
		||||
const CALL_BEHAVIOUR_WK_KEY = "io.element.call_behaviour";
 | 
			
		||||
const E2EE_WK_KEY = "io.element.e2ee";
 | 
			
		||||
const E2EE_WK_KEY_DEPRECATED = "im.vector.riot.e2ee";
 | 
			
		||||
const TILE_SERVER_WK_KEY = new UnstableValue(
 | 
			
		||||
    "m.tile_server", "org.matrix.msc3488.tile_server");
 | 
			
		||||
 | 
			
		||||
/* eslint-disable camelcase */
 | 
			
		||||
export interface ICallBehaviourWellKnown {
 | 
			
		||||
@@ -30,6 +35,10 @@ export interface IE2EEWellKnown {
 | 
			
		||||
    secure_backup_required?: boolean;
 | 
			
		||||
    secure_backup_setup_methods?: SecureBackupSetupMethod[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITileServerWellKnown {
 | 
			
		||||
    map_style_url?: string;
 | 
			
		||||
}
 | 
			
		||||
/* eslint-enable camelcase */
 | 
			
		||||
 | 
			
		||||
export function getCallBehaviourWellKnown(): ICallBehaviourWellKnown {
 | 
			
		||||
@@ -48,6 +57,19 @@ export function getE2EEWellKnown(): IE2EEWellKnown {
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getTileServerWellKnown(): ITileServerWellKnown | undefined {
 | 
			
		||||
    return tileServerFromWellKnown(MatrixClientPeg.get().getClientWellKnown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function tileServerFromWellKnown(
 | 
			
		||||
    clientWellKnown?: IClientWellKnown | undefined,
 | 
			
		||||
): ITileServerWellKnown {
 | 
			
		||||
    return (
 | 
			
		||||
        clientWellKnown?.[TILE_SERVER_WK_KEY.name] ??
 | 
			
		||||
        clientWellKnown?.[TILE_SERVER_WK_KEY.altName]
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isSecureBackupRequired(): boolean {
 | 
			
		||||
    const wellKnown = getE2EEWellKnown();
 | 
			
		||||
    return wellKnown && wellKnown["secure_backup_required"] === true;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user