leave maximised map when beacons expire (#9098)
parent
ca1d9729fd
commit
0357b4f0dc
|
@ -55,3 +55,8 @@ limitations under the License.
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_DialogSidebar_noResults {
|
||||||
|
font-size: $font-14px;
|
||||||
|
color: $secondary-content;
|
||||||
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue