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

Live location share: leave maximised map open when beacons expire (PSF-615) #9098

Merged
merged 7 commits into from
Jul 27, 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
5 changes: 5 additions & 0 deletions res/css/components/views/beacon/_DialogSidebar.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ limitations under the License.
overflow: auto;
}
}

.mx_DialogSidebar_noResults {
font-size: $font-14px;
color: $secondary-content;
}
8 changes: 4 additions & 4 deletions src/components/views/beacon/BeaconViewDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
} => {
const [bounds, setBounds] = useState<Bounds | undefined>(getBeaconBounds(liveBeacons));
Expand Down Expand Up @@ -113,7 +113,7 @@ const BeaconViewDialog: React.FC<IProps> = ({

const [isSidebarOpen, setSidebarOpen] = useState(false);

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

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

Expand All @@ -135,7 +135,7 @@ const BeaconViewDialog: React.FC<IProps> = ({
fixedWidth={false}
>
<MatrixClientContext.Provider value={matrixClient}>
{ (!!liveBeacons?.length && !mapDisplayError) && <Map
{ (centerGeoUri && !mapDisplayError) && <Map
id='mx_BeaconViewDialog'
bounds={bounds}
centerGeoUri={centerGeoUri}
Expand All @@ -162,7 +162,7 @@ const BeaconViewDialog: React.FC<IProps> = ({
isMinimised
/>
}
{ !liveBeacons?.length && !mapDisplayError &&
{ !centerGeoUri && !mapDisplayError &&
<MapFallback
data-test-id='beacon-view-dialog-map-fallback'
className='mx_BeaconViewDialog_map'
Expand Down
3 changes: 1 addition & 2 deletions src/components/views/beacon/DialogOwnBeaconStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { LocationAssetType } from 'matrix-js-sdk/src/@types/location';

import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
import OwnBeaconStatus from './OwnBeaconStatus';
import { BeaconDisplayStatus } from './displayStatus';
import MatrixClientContext from '../../../contexts/MatrixClientContext';
Expand All @@ -33,7 +32,7 @@ interface Props {

const useOwnBeacon = (roomId: Room['roomId']): Beacon | undefined => {
const ownBeacon = useEventEmitterState(
OwnProfileStore.instance,
OwnBeaconStore.instance,
OwnBeaconStoreEvent.LivenessChange,
() => {
const [ownBeaconId] = OwnBeaconStore.instance.getLiveBeaconIds(roomId);
Expand Down
19 changes: 12 additions & 7 deletions src/components/views/beacon/DialogSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,18 @@ const DialogSidebar: React.FC<Props> = ({
<CloseIcon className='mx_DialogSidebar_closeButtonIcon' />
</AccessibleButton>
</div>
<ol className='mx_DialogSidebar_list'>
{ beacons.map((beacon) => <BeaconListItem
key={beacon.identifier}
beacon={beacon}
onClick={() => onBeaconClick(beacon)}
/>) }
</ol>
{ beacons?.length
? <ol className='mx_DialogSidebar_list'>
{ beacons.map((beacon) => <BeaconListItem
key={beacon.identifier}
beacon={beacon}
onClick={() => onBeaconClick(beacon)}
/>) }
</ol>
: <div className='mx_DialogSidebar_noResults'>
{ _t('No live locations') }
</div>
}
</div>;
};

Expand Down
43 changes: 33 additions & 10 deletions test/components/views/beacon/BeaconViewDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
getBeaconInfoIdentifier,
} from 'matrix-js-sdk/src/matrix';
import maplibregl from 'maplibre-gl';
import { mocked } from 'jest-mock';

import BeaconViewDialog from '../../../../src/components/views/beacon/BeaconViewDialog';
import {
Expand Down Expand Up @@ -103,6 +104,7 @@ describe('<BeaconViewDialog />', () => {

beforeEach(() => {
jest.spyOn(OwnBeaconStore.instance, 'getLiveBeaconIds').mockRestore();
jest.spyOn(OwnBeaconStore.instance, 'getBeaconById').mockRestore();
jest.spyOn(global.Date, 'now').mockReturnValue(now);
jest.clearAllMocks();
});
Expand Down Expand Up @@ -193,7 +195,24 @@ describe('<BeaconViewDialog />', () => {
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 room = setupRoom([defaultEvent]);
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
Expand All @@ -206,24 +225,28 @@ describe('<BeaconViewDialog />', () => {
const anotherBeaconEvent = makeBeaconInfoEvent(aliceId,
roomId,
{ 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(() => {
// emits RoomStateEvent.BeaconLiveness
room.currentState.setStateEvents([anotherBeaconEvent]);
});

component.setProps({});

// 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();
// no more avatars
expect(component.find('MemberAvatar').length).toBeFalsy();
// map still rendered
expect(component.find('Map').length).toBeTruthy();
// map location unchanged
expect(mockMap.setCenter).not.toHaveBeenCalled();
expect(mockMap.fitBounds).not.toHaveBeenCalled();
});

describe('sidebar', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// 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 [
<MapFallback
className="mx_BeaconViewDialog_map"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,11 @@ exports[`<DialogSidebar /> renders sidebar correctly without beacons 1`] = `
/>
</div>
</div>
<ol
class="mx_DialogSidebar_list"
/>
<div
class="mx_DialogSidebar_noResults"
>
No live locations
</div>
</div>
);
</div>
Expand Down