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

fallback to event text in location body when map unavailable #7982

Merged
merged 27 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import "./_font-weights.scss";
@import "./_spacing.scss";
@import "./components/views/location/_LocationShareMenu.scss";
@import "./components/views/location/_MapError.scss";
@import "./components/views/location/_ShareDialogButtons.scss";
@import "./components/views/location/_ShareType.scss";
@import "./components/views/spaces/_QuickThemeSwitcher.scss";
Expand Down
36 changes: 36 additions & 0 deletions res/css/components/views/location/_MapError.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
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.
*/

.mx_MapError {
padding: 100px $spacing-32 0;
text-align: center;

p {
margin: $spacing-16 0 $spacing-32;
}
}

.mx_MapError_heading {
padding-top: $spacing-24;
}

.mx_MapError_icon {
height: 58px;

path {
fill: $secondary-content;
}
}
15 changes: 10 additions & 5 deletions res/css/views/location/_LocationPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ limitations under the License.
position: relative;
overflow: hidden;

// when there are errors loading the map
// the canvas is still inserted
// and can overlap error message/close buttons
// hide it
&.mx_LocationPicker_hasError {
.maplibregl-canvas-container, .maplibregl-control-container {
display: none;
}
}

#mx_LocationPicker_map {
height: 100%;
border-radius: 8px;
Expand Down Expand Up @@ -94,11 +104,6 @@ limitations under the License.

background-color: $header-panel-bg-color;
}

.mx_LocationPicker_error {
color: red;
margin: auto;
}
}

.mx_MLocationBody_markerIcon {
Expand Down
28 changes: 17 additions & 11 deletions src/components/views/location/LocationPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ 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';
import { findMapStyleUrl } from './findMapStyleUrl';
import { LocationShareType } from './shareLocation';
import { Icon as LocationIcon } from '../../../../res/img/element-icons/location.svg';
import { LocationShareError } from './LocationShareErrors';
import AccessibleButton from '../elements/AccessibleButton';

import { MapError } from './MapError';
export interface ILocationPickerProps {
sender: RoomMember;
shareType: LocationShareType;
Expand All @@ -48,7 +49,7 @@ interface IPosition {
}
interface IState {
position?: IPosition;
error: Error;
error?: LocationShareError;
}

/*
Expand Down Expand Up @@ -104,10 +105,10 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
this.map.on('error', (e) => {
logger.error(
"Failed to load map: check map_style_url in config.json "
+ "has a valid URL and API key",
+ "has a valid URL and API key",
e.error,
);
this.setState({ error: e.error });
this.setState({ error: LocationShareError.MapStyleUrlNotReachable });
});

this.map.on('load', () => {
Expand All @@ -129,7 +130,10 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
}
} catch (e) {
logger.error("Failed to render map", e);
this.setState({ error: e });
const errorType = e?.message === LocationShareError.MapStyleUrlNotConfigured ?
LocationShareError.MapStyleUrlNotConfigured :
LocationShareError.Default;
this.setState({ error: errorType });
}
}

Expand Down Expand Up @@ -213,10 +217,13 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
};

render() {
const error = this.state.error ?
<div data-test-id='location-picker-error' className="mx_LocationPicker_error">
{ _t("Failed to load map") }
</div> : null;
if (this.state.error) {
return <div className="mx_LocationPicker mx_LocationPicker_hasError">
<MapError
error={this.state.error}
onFinished={this.props.onFinished} />
</div>;
}

return (
<div className="mx_LocationPicker">
Expand All @@ -227,7 +234,6 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
</span>
</div>
}
{ error }
<div className="mx_LocationPicker_footer">
<form onSubmit={this.onOk}>

Expand Down
34 changes: 34 additions & 0 deletions src/components/views/location/LocationShareErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
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 { _t } from "../../../languageHandler";

export enum LocationShareError {
MapStyleUrlNotConfigured = 'MapStyleUrlNotConfigured',
MapStyleUrlNotReachable = 'MapStyleUrlNotReachable',
Default = 'Default'
}

export const getLocationShareErrorMessage = (errorType?: LocationShareError): string => {
switch (errorType) {
case LocationShareError.MapStyleUrlNotConfigured:
return _t('This homeserver is not configured to display maps.');
case LocationShareError.MapStyleUrlNotReachable:
default:
return _t(`This homeserver is not configured correctly to display maps, `
+ `or the configured map server may be unreachable.`);
}
};
39 changes: 39 additions & 0 deletions src/components/views/location/MapError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
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 React from 'react';

import { Icon as WarningBadge } from '../../../../res/img/element-icons/warning-badge.svg';
import { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
import Heading from '../typography/Heading';
import { getLocationShareErrorMessage, LocationShareError } from './LocationShareErrors';

interface Props {
onFinished: () => void;
error: LocationShareError;
}

export const MapError: React.FC<Props> = ({
onFinished, error,
}) => (<div data-test-id='location-picker-error' className="mx_MapError">
<WarningBadge className="mx_MapError_icon" />
<Heading className="mx_MapError_heading" size='h3'>{ _t("Unable to load map") }</Heading>
<p>
{ getLocationShareErrorMessage(error) }
</p>
<AccessibleButton element='button' kind="primary" onClick={onFinished}>{ _t("OK") }</AccessibleButton>
</div>);
41 changes: 41 additions & 0 deletions src/components/views/location/findMapStyleUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
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 { logger } from "matrix-js-sdk/src/logger";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs copyright


import SdkConfig from "../../../SdkConfig";
import { getTileServerWellKnown } from "../../../utils/WellKnownUtils";
import { LocationShareError } from "./LocationShareErrors";

/**
* 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) {
logger.error("'map_style_url' missing from homeserver .well-known area, and " +
"missing from from config.json.");
throw new Error(LocationShareError.MapStyleUrlNotConfigured);
}

return mapStyleUrl;
}
70 changes: 31 additions & 39 deletions src/components/views/messages/MLocationBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
} from 'matrix-js-sdk/src/@types/location';
import { ClientEvent, IClientWellKnown } from 'matrix-js-sdk/src/client';

import SdkConfig from '../../../SdkConfig';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { IBodyProps } from "./IBodyProps";
import { _t } from '../../../languageHandler';
Expand All @@ -36,8 +35,10 @@ 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 { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { findMapStyleUrl } from '../location/findMapStyleUrl';
import { getLocationShareErrorMessage, LocationShareError } from '../location/LocationShareErrors';

interface IState {
error: Error;
Expand Down Expand Up @@ -117,14 +118,16 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
};

render(): React.ReactElement<HTMLDivElement> {
return <LocationBodyContent
mxEvent={this.props.mxEvent}
bodyId={this.bodyId}
markerId={this.markerId}
error={this.state.error}
tooltip={_t("Expand map")}
onClick={this.onClick}
/>;
return this.state.error ?
<LocationBodyFallbackContent error={this.state.error} event={this.props.mxEvent} /> :
<LocationBodyContent
mxEvent={this.props.mxEvent}
bodyId={this.bodyId}
markerId={this.markerId}
error={this.state.error}
tooltip={_t("Expand map")}
onClick={this.onClick}
/>;
}
}

Expand All @@ -146,6 +149,23 @@ interface ILocationBodyContentProps {
onZoomOut?: () => void;
}

export const LocationBodyFallbackContent: React.FC<{ event: MatrixEvent, error: Error }> = ({ error, event }) => {
const errorType = error?.message as LocationShareError;
const message = `${_t('Unable to load map')}: ${getLocationShareErrorMessage(errorType)}`;

const locationFallback = isSelfLocation(event.getContent()) ?
(_t('Shared their location: ') + event.getContent()?.body) :
(_t('Shared a location: ') + event.getContent()?.body);

return <div className="mx_EventTile_body">
<span className={errorType !== LocationShareError.MapStyleUrlNotConfigured ? "mx_EventTile_tileError" : ''}>
{ message }
</span>
<br />
{ locationFallback }
</div>;
};

export function LocationBodyContent(props: ILocationBodyContentProps):
React.ReactElement<HTMLDivElement> {
const mapDiv = <div
Expand All @@ -166,13 +186,6 @@ export function LocationBodyContent(props: ILocationBodyContentProps):
);

return <div className="mx_MLocationBody">
{
props.error
? <div className="mx_EventTile_tileError mx_EventTile_body">
{ _t("Failed to load map") }
</div>
: null
}
{
props.tooltip
? <TooltipTarget
Expand Down Expand Up @@ -225,27 +238,6 @@ 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,
Expand Down Expand Up @@ -279,7 +271,7 @@ export function createMap(
+ "valid URL and API key",
e.error,
);
onError(e.error);
onError(new Error(LocationShareError.MapStyleUrlNotReachable));
});

return map;
Expand Down
Loading