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

Live location share - tiles without tile server (PSG-591) #8962

Merged
merged 16 commits into from
Jul 6, 2022
Merged
4 changes: 4 additions & 0 deletions res/css/components/views/beacon/_OwnBeaconStatus.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

.mx_OwnBeaconStatus_button {
margin-left: $spacing-8;
}

.mx_EventTile[data-layout="bubble"] .mx_OwnBeaconStatus_button {
// align to top to make room for timestamp
// in bubble view
Expand Down
40 changes: 36 additions & 4 deletions res/css/components/views/location/_MapError.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,51 @@ limitations under the License.
padding: 100px $spacing-32 0;
text-align: center;

p {
margin: $spacing-16 0 $spacing-32;
--mx-map-error-icon-color: $secondary-content;
--mx-map-error-icon-size: 58px;
}

.mx_MapError.mx_MapError_isMinimised {
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;

padding: $spacing-24;
background-color: $panels;
font-size: $font-12px;
line-height: $font-16px;

--mx-map-error-icon-color: $alert;
--mx-map-error-icon-size: 26px;

.mx_MapError_message {
margin: 0;
max-width: 275px;
}

.mx_MapError_heading {
padding-top: $spacing-8;
// override h3 heading size
font-size: inherit !important;
font-weight: normal !important;
Comment on lines +48 to +50
Copy link
Member

Choose a reason for hiding this comment

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

If we're doing this, what's the reason for making it an h3?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the original/non-minimised version of the component the h3 style is not overriden

}
}

.mx_MapError_message {
margin: $spacing-16 0 $spacing-32;
}

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

.mx_MapError_icon {
height: 58px;
height: var(--mx-map-error-icon-size);

path {
fill: $secondary-content;
fill: var(--mx-map-error-icon-color);
}
}
34 changes: 32 additions & 2 deletions res/css/components/views/messages/_MBeaconBody.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ limitations under the License.
overflow: hidden;
}

.mx_MBeaconBody.mx_MBeaconBody_withoutMap {
height: auto;

.mx_MBeaconBody_chin {
position: relative;
background-color: transparent;
}
}

.mx_MBeaconBody_withoutMapContent {
background-color: $panels;
border-radius: 4px;
}

.mx_MBeaconBody_withoutMapInfoLastUpdated {
// 48px lines up with icon in BeaconStatus
margin-top: -$spacing-8;
padding: 0 $spacing-8 $spacing-8 48px;

color: $tertiary-content;
font-size: $font-10px;
}

.mx_MBeaconBody_map {
height: 100%;
width: 100%;
Expand All @@ -32,11 +55,18 @@ limitations under the License.
cursor: pointer;
}

.mx_MBeaconBody_mapFallback {
.mx_MBeaconBody_mapFallback,
.mx_MBeaconBody_mapError {
// pushes spinner/icon up
// to appear more centered with the footer
padding-bottom: 50px;
padding-bottom: 50px !important;
}

.mx_MBeaconBody_mapErrorInteractive {
cursor: pointer;
}

.mx_MBeaconBody_mapFallback {
cursor: default;
}

Expand Down
25 changes: 22 additions & 3 deletions src/components/views/beacon/BeaconViewDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { useState, useRef } from 'react';
import React, { useState, useRef, useEffect } from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import {
Beacon,
Expand All @@ -38,6 +38,8 @@ import DialogSidebar from './DialogSidebar';
import DialogOwnBeaconStatus from './DialogOwnBeaconStatus';
import BeaconStatusTooltip from './BeaconStatusTooltip';
import MapFallback from '../location/MapFallback';
import { MapError } from '../location/MapError';
import { LocationShareError } from '../../../utils/location';

interface IProps extends IDialogProps {
roomId: Room['roomId'];
Expand Down Expand Up @@ -83,18 +85,28 @@ const BeaconViewDialog: React.FC<IProps> = ({

const { bounds, centerGeoUri } = useInitialMapPosition(liveBeacons, focusBeacon);

const [mapDisplayError, setMapDisplayError] = useState<Error>();

// automatically open the sidebar if there is no map to see
useEffect(() => {
if (mapDisplayError) {
setSidebarOpen(true);
}
}, [mapDisplayError]);

return (
<BaseDialog
className='mx_BeaconViewDialog'
onFinished={onFinished}
fixedWidth={false}
>
<MatrixClientContext.Provider value={matrixClient}>
{ !!liveBeacons?.length ? <Map
{ (!!liveBeacons?.length && !mapDisplayError) && <Map
id='mx_BeaconViewDialog'
bounds={bounds}
centerGeoUri={centerGeoUri}
interactive
onError={setMapDisplayError}
className="mx_BeaconViewDialog_map"
>
{
Expand All @@ -109,7 +121,14 @@ const BeaconViewDialog: React.FC<IProps> = ({
<ZoomButtons map={map} />
</>
}
</Map> :
</Map> }
{ mapDisplayError &&
<MapError
error={mapDisplayError.message as LocationShareError}
isMinimised
/>
}
{ !liveBeacons?.length && !mapDisplayError &&
<MapFallback
data-test-id='beacon-view-dialog-map-fallback'
className='mx_BeaconViewDialog_map'
Expand Down
41 changes: 31 additions & 10 deletions src/components/views/location/MapError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
*/

import React from 'react';
import classNames from 'classnames';

import { Icon as WarningBadge } from '../../../../res/img/element-icons/warning-badge.svg';
import { _t } from '../../../languageHandler';
Expand All @@ -23,17 +24,37 @@ import AccessibleButton from '../elements/AccessibleButton';
import Heading from '../typography/Heading';

interface Props {
onFinished: () => void;
error: LocationShareError;
onFinished?: () => void;
isMinimised?: boolean;
className?: string;
onClick?: () => void;
}

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>);
error,
isMinimised,
className,
onFinished,
onClick,
}) => (
<div data-test-id='map-rendering-error'
className={classNames('mx_MapError', className, { 'mx_MapError_isMinimised': isMinimised })}
onClick={onClick}
>
<WarningBadge className='mx_MapError_icon' />
<Heading className='mx_MapError_heading' size='h3'>{ _t('Unable to load map') }</Heading>
<p className='mx_MapError_message'>
{ getLocationShareErrorMessage(error) }
</p>
{ onFinished &&
<AccessibleButton
element='button'
kind='primary'
onClick={onFinished}
>
{ _t('OK') }
</AccessibleButton>
}
</div>
);
1 change: 0 additions & 1 deletion src/components/views/location/MapFallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
const MapFallback: React.FC<Props> = ({ className, isLoading, children, ...rest }) => {
return <div className={classNames('mx_MapFallback', className)} {...rest}>
<MapFallbackImage className='mx_MapFallback_bg' />
{ /* <div className='mx_MapFallback_bg'/> */ }
{ isLoading ? <Spinner h={32} w={32} /> : <LocationMarkerIcon className='mx_MapFallback_icon' /> }
{ children }
</div>;
Expand Down
44 changes: 36 additions & 8 deletions src/components/views/messages/MBeaconBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ import {
import { BeaconLocationState } from 'matrix-js-sdk/src/content-helpers';
import { randomString } from 'matrix-js-sdk/src/randomstring';
import { M_BEACON } from 'matrix-js-sdk/src/@types/beacon';
import classNames from 'classnames';

import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
import { isBeaconWaitingToStart, useBeacon } from '../../../utils/beacon';
import { isSelfLocation } from '../../../utils/location';
import { isSelfLocation, LocationShareError } from '../../../utils/location';
import { BeaconDisplayStatus, getBeaconDisplayStatus } from '../beacon/displayStatus';
import BeaconStatus from '../beacon/BeaconStatus';
import OwnBeaconStatus from '../beacon/OwnBeaconStatus';
import Map from '../location/Map';
import { MapError } from '../location/MapError';
import MapFallback from '../location/MapFallback';
import SmartMarker from '../location/SmartMarker';
import { GetRelationsForEvent } from '../rooms/EventTile';
Expand Down Expand Up @@ -136,7 +138,16 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati

const matrixClient = useContext(MatrixClientContext);
const [error, setError] = useState<Error>();
const displayStatus = getBeaconDisplayStatus(isLive, latestLocationState, error, waitingToStart);
const isMapDisplayError = error?.message === LocationShareError.MapStyleUrlNotConfigured ||
error?.message === LocationShareError.MapStyleUrlNotReachable;
const displayStatus = getBeaconDisplayStatus(
isLive,
latestLocationState,
// if we are unable to display maps because it is not configured for the server
// don't display an error
isMapDisplayError ? undefined : error,
waitingToStart,
);
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
const isOwnBeacon = beacon?.beaconInfoOwner === matrixClient.getUserId();

Expand All @@ -152,6 +163,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
roomId: mxEvent.getRoomId(),
matrixClient,
focusBeacon: beacon,
isMapDisplayError,
},
"mx_BeaconViewDialog_wrapper",
false, // isPriority
Expand All @@ -160,8 +172,11 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
};

return (
<div className='mx_MBeaconBody' ref={ref}>
{ displayStatus === BeaconDisplayStatus.Active ?
<div
className='mx_MBeaconBody'
ref={ref}
>
{ (displayStatus === BeaconDisplayStatus.Active && !isMapDisplayError) ?
<Map
id={mapId}
centerGeoUri={latestLocationState.uri}
Expand All @@ -180,10 +195,23 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
/>
}
</Map>
: <MapFallback
isLoading={displayStatus === BeaconDisplayStatus.Loading}
className='mx_MBeaconBody_map mx_MBeaconBody_mapFallback'
/>
: isMapDisplayError ?
<MapError
error={error.message as LocationShareError}
onClick={onClick}
className={classNames(
'mx_MBeaconBody_mapError',
// set interactive class when maximised map can be opened
{ 'mx_MBeaconBody_mapErrorInteractive':
displayStatus === BeaconDisplayStatus.Active,
},
)}
isMinimised
/> :
<MapFallback
isLoading={displayStatus === BeaconDisplayStatus.Loading}
className='mx_MBeaconBody_map mx_MBeaconBody_mapFallback'
/>
}
{ isOwnBeacon ?
<OwnBeaconStatus
Expand Down
6 changes: 3 additions & 3 deletions test/components/views/location/LocationPicker-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("LocationPicker", () => {
wrapper.setProps({});
});

expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
"This homeserver is not configured correctly to display maps, "
+ "or the configured map server may be unreachable.",
);
Expand All @@ -115,7 +115,7 @@ describe("LocationPicker", () => {
const wrapper = getComponent();
wrapper.setProps({});

expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
"This homeserver is not configured to display maps.",
);
});
Expand All @@ -130,7 +130,7 @@ describe("LocationPicker", () => {
const wrapper = getComponent();
wrapper.setProps({});

expect(findByTestId(wrapper, 'location-picker-error').find('p').text()).toEqual(
expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual(
"This homeserver is not configured correctly to display maps, "
+ "or the configured map server may be unreachable.",
);
Expand Down
Loading