leave maximised map when beacons expire (#9098)

pull/28788/head^2
Kerry 2022-07-27 17:42:59 +02:00 committed by GitHub
parent ca1d9729fd
commit 0357b4f0dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 61 additions and 27 deletions

View File

@ -55,3 +55,8 @@ limitations under the License.
overflow: auto; overflow: auto;
} }
} }
.mx_DialogSidebar_noResults {
font-size: $font-14px;
color: $secondary-content;
}

View File

@ -68,7 +68,7 @@ const getBoundsCenter = (bounds: Bounds): string | undefined => {
}); });
}; };
const useInitialMapPosition = (liveBeacons: Beacon[], { beacon, ts }: FocusedBeaconState): { const useMapPosition = (liveBeacons: Beacon[], { beacon, ts }: FocusedBeaconState): {
bounds?: Bounds; centerGeoUri: string; bounds?: Bounds; centerGeoUri: string;
} => { } => {
const [bounds, setBounds] = useState<Bounds | undefined>(getBeaconBounds(liveBeacons)); const [bounds, setBounds] = useState<Bounds | undefined>(getBeaconBounds(liveBeacons));
@ -113,7 +113,7 @@ const BeaconViewDialog: React.FC<IProps> = ({
const [isSidebarOpen, setSidebarOpen] = useState(false); const [isSidebarOpen, setSidebarOpen] = useState(false);
const { bounds, centerGeoUri } = useInitialMapPosition(liveBeacons, focusedBeaconState); const { bounds, centerGeoUri } = useMapPosition(liveBeacons, focusedBeaconState);
const [mapDisplayError, setMapDisplayError] = useState<Error>(); const [mapDisplayError, setMapDisplayError] = useState<Error>();
@ -135,7 +135,7 @@ const BeaconViewDialog: React.FC<IProps> = ({
fixedWidth={false} fixedWidth={false}
> >
<MatrixClientContext.Provider value={matrixClient}> <MatrixClientContext.Provider value={matrixClient}>
{ (!!liveBeacons?.length && !mapDisplayError) && <Map { (centerGeoUri && !mapDisplayError) && <Map
id='mx_BeaconViewDialog' id='mx_BeaconViewDialog'
bounds={bounds} bounds={bounds}
centerGeoUri={centerGeoUri} centerGeoUri={centerGeoUri}
@ -162,7 +162,7 @@ const BeaconViewDialog: React.FC<IProps> = ({
isMinimised isMinimised
/> />
} }
{ !liveBeacons?.length && !mapDisplayError && { !centerGeoUri && !mapDisplayError &&
<MapFallback <MapFallback
data-test-id='beacon-view-dialog-map-fallback' data-test-id='beacon-view-dialog-map-fallback'
className='mx_BeaconViewDialog_map' className='mx_BeaconViewDialog_map'

View File

@ -20,7 +20,6 @@ import { LocationAssetType } from 'matrix-js-sdk/src/@types/location';
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore'; import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
import { useEventEmitterState } from '../../../hooks/useEventEmitter'; import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
import OwnBeaconStatus from './OwnBeaconStatus'; import OwnBeaconStatus from './OwnBeaconStatus';
import { BeaconDisplayStatus } from './displayStatus'; import { BeaconDisplayStatus } from './displayStatus';
import MatrixClientContext from '../../../contexts/MatrixClientContext'; import MatrixClientContext from '../../../contexts/MatrixClientContext';
@ -33,7 +32,7 @@ interface Props {
const useOwnBeacon = (roomId: Room['roomId']): Beacon | undefined => { const useOwnBeacon = (roomId: Room['roomId']): Beacon | undefined => {
const ownBeacon = useEventEmitterState( const ownBeacon = useEventEmitterState(
OwnProfileStore.instance, OwnBeaconStore.instance,
OwnBeaconStoreEvent.LivenessChange, OwnBeaconStoreEvent.LivenessChange,
() => { () => {
const [ownBeaconId] = OwnBeaconStore.instance.getLiveBeaconIds(roomId); const [ownBeaconId] = OwnBeaconStore.instance.getLiveBeaconIds(roomId);

View File

@ -46,13 +46,18 @@ const DialogSidebar: React.FC<Props> = ({
<CloseIcon className='mx_DialogSidebar_closeButtonIcon' /> <CloseIcon className='mx_DialogSidebar_closeButtonIcon' />
</AccessibleButton> </AccessibleButton>
</div> </div>
<ol className='mx_DialogSidebar_list'> { beacons?.length
? <ol className='mx_DialogSidebar_list'>
{ beacons.map((beacon) => <BeaconListItem { beacons.map((beacon) => <BeaconListItem
key={beacon.identifier} key={beacon.identifier}
beacon={beacon} beacon={beacon}
onClick={() => onBeaconClick(beacon)} onClick={() => onBeaconClick(beacon)}
/>) } />) }
</ol> </ol>
: <div className='mx_DialogSidebar_noResults'>
{ _t('No live locations') }
</div>
}
</div>; </div>;
}; };

View File

@ -25,6 +25,7 @@ import {
getBeaconInfoIdentifier, getBeaconInfoIdentifier,
} from 'matrix-js-sdk/src/matrix'; } from 'matrix-js-sdk/src/matrix';
import maplibregl from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { mocked } from 'jest-mock';
import BeaconViewDialog from '../../../../src/components/views/beacon/BeaconViewDialog'; import BeaconViewDialog from '../../../../src/components/views/beacon/BeaconViewDialog';
import { import {
@ -103,6 +104,7 @@ describe('<BeaconViewDialog />', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(OwnBeaconStore.instance, 'getLiveBeaconIds').mockRestore(); jest.spyOn(OwnBeaconStore.instance, 'getLiveBeaconIds').mockRestore();
jest.spyOn(OwnBeaconStore.instance, 'getBeaconById').mockRestore();
jest.spyOn(global.Date, 'now').mockReturnValue(now); jest.spyOn(global.Date, 'now').mockReturnValue(now);
jest.clearAllMocks(); jest.clearAllMocks();
}); });
@ -193,7 +195,24 @@ describe('<BeaconViewDialog />', () => {
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1); expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
}); });
it('renders a fallback when no live beacons remain', () => { it('renders a fallback when there are no locations', () => {
// this is a cornercase, should not be a reachable state in UI anymore
const onFinished = jest.fn();
const room = setupRoom([defaultEvent]);
room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
const component = getComponent({ onFinished });
// map placeholder
expect(findByTestId(component, 'beacon-view-dialog-map-fallback')).toMatchSnapshot();
act(() => {
findByTestId(component, 'beacon-view-dialog-fallback-close').at(0).simulate('click');
});
expect(onFinished).toHaveBeenCalled();
});
it('renders map without markers when no live beacons remain', () => {
const onFinished = jest.fn(); const onFinished = jest.fn();
const room = setupRoom([defaultEvent]); const room = setupRoom([defaultEvent]);
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent)); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
@ -206,9 +225,14 @@ describe('<BeaconViewDialog />', () => {
const anotherBeaconEvent = makeBeaconInfoEvent(aliceId, const anotherBeaconEvent = makeBeaconInfoEvent(aliceId,
roomId, roomId,
{ isLive: false }, { isLive: false },
'$bob-room1-1', '$alice-room1-2',
); );
expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 41 });
// reset call counts
mocked(mockMap.setCenter).mockClear();
mocked(mockMap.fitBounds).mockClear();
act(() => { act(() => {
// emits RoomStateEvent.BeaconLiveness // emits RoomStateEvent.BeaconLiveness
room.currentState.setStateEvents([anotherBeaconEvent]); room.currentState.setStateEvents([anotherBeaconEvent]);
@ -216,14 +240,13 @@ describe('<BeaconViewDialog />', () => {
component.setProps({}); component.setProps({});
// map placeholder // no more avatars
expect(findByTestId(component, 'beacon-view-dialog-map-fallback')).toMatchSnapshot(); expect(component.find('MemberAvatar').length).toBeFalsy();
// map still rendered
act(() => { expect(component.find('Map').length).toBeTruthy();
findByTestId(component, 'beacon-view-dialog-fallback-close').at(0).simulate('click'); // map location unchanged
}); expect(mockMap.setCenter).not.toHaveBeenCalled();
expect(mockMap.fitBounds).not.toHaveBeenCalled();
expect(onFinished).toHaveBeenCalled();
}); });
describe('sidebar', () => { describe('sidebar', () => {

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<BeaconViewDialog /> renders a fallback when no live beacons remain 1`] = ` exports[`<BeaconViewDialog /> renders a fallback when there are no locations 1`] = `
Array [ Array [
<MapFallback <MapFallback
className="mx_BeaconViewDialog_map" className="mx_BeaconViewDialog_map"

View File

@ -135,9 +135,11 @@ exports[`<DialogSidebar /> renders sidebar correctly without beacons 1`] = `
/> />
</div> </div>
</div> </div>
<ol <div
class="mx_DialogSidebar_list" class="mx_DialogSidebar_noResults"
/> >
No live locations
</div>
</div> </div>
); );
</div> </div>