Apply strictNullChecks to src/components/views/location/* (#10249

* strict fixes

* accessiblebutton without onClick explicit

* strict fix for UserMenu BaseAvatar
pull/28788/head^2
Kerry 2023-02-28 21:55:59 +13:00 committed by GitHub
parent 6de8d85f7e
commit dd6fc124d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 46 additions and 31 deletions

View File

@ -432,7 +432,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
public render(): React.ReactNode { public render(): React.ReactNode {
const avatarSize = 32; // should match border-radius of the avatar const avatarSize = 32; // should match border-radius of the avatar
const userId = MatrixClientPeg.get().getUserId(); const userId = MatrixClientPeg.get().getSafeUserId();
const displayName = OwnProfileStore.instance.displayName || userId; const displayName = OwnProfileStore.instance.displayName || userId;
const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize);

View File

@ -32,7 +32,7 @@ import { toPx } from "../../../utils/units";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
interface IProps { interface IProps {
name: string; // The name (first initial used as default) name?: string; // The name (first initial used as default)
idName?: string; // ID for generating hash colours idName?: string; // ID for generating hash colours
title?: string; // onHover title text title?: string; // onHover title text
url?: string; // highest priority of them all, shortcut to set in urls[0] url?: string; // highest priority of them all, shortcut to set in urls[0]

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { ReactElement, SyntheticEvent, useContext } from "react"; import React, { ReactNode, SyntheticEvent, useContext } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { IEventRelation } from "matrix-js-sdk/src/models/event"; import { IEventRelation } from "matrix-js-sdk/src/models/event";
@ -41,9 +41,9 @@ export const LocationButton: React.FC<IProps> = ({ roomId, sender, menuPosition,
overflowMenuCloser?.(); overflowMenuCloser?.();
}; };
let contextMenu: ReactElement; let contextMenu: ReactNode = null;
if (menuDisplayed) { if (menuDisplayed) {
const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()); const position = menuPosition ?? (button.current && aboveLeftOf(button.current.getBoundingClientRect())) ?? {};
contextMenu = ( contextMenu = (
<LocationShareMenu <LocationShareMenu

View File

@ -52,9 +52,9 @@ const isSharingOwnLocation = (shareType: LocationShareType): boolean =>
class LocationPicker extends React.Component<ILocationPickerProps, IState> { class LocationPicker extends React.Component<ILocationPickerProps, IState> {
public static contextType = MatrixClientContext; public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>; public context!: React.ContextType<typeof MatrixClientContext>;
private map?: maplibregl.Map = null; private map?: maplibregl.Map;
private geolocate?: maplibregl.GeolocateControl = null; private geolocate?: maplibregl.GeolocateControl;
private marker?: maplibregl.Marker = null; private marker?: maplibregl.Marker;
public constructor(props: ILocationPickerProps) { public constructor(props: ILocationPickerProps) {
super(props); super(props);
@ -100,7 +100,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
}); });
this.map.on("load", () => { this.map.on("load", () => {
this.geolocate.trigger(); this.geolocate?.trigger();
}); });
this.geolocate.on("error", this.onGeolocateError); this.geolocate.on("error", this.onGeolocateError);
@ -120,7 +120,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
} catch (e) { } catch (e) {
logger.error("Failed to render map", e); logger.error("Failed to render map", e);
const errorType = const errorType =
e?.message === LocationShareError.MapStyleUrlNotConfigured (e as Error)?.message === LocationShareError.MapStyleUrlNotConfigured
? LocationShareError.MapStyleUrlNotConfigured ? LocationShareError.MapStyleUrlNotConfigured
: LocationShareError.Default; : LocationShareError.Default;
this.setState({ error: errorType }); this.setState({ error: errorType });
@ -141,7 +141,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
offset: [0, -1], offset: [0, -1],
}) })
.setLngLat(new maplibregl.LngLat(0, 0)) .setLngLat(new maplibregl.LngLat(0, 0))
.addTo(this.map); .addTo(this.map!);
}; };
private updateStyleUrl = (clientWellKnown: IClientWellKnown): void => { private updateStyleUrl = (clientWellKnown: IClientWellKnown): void => {

View File

@ -64,14 +64,15 @@ const LocationShareMenu: React.FC<Props> = ({ menuPosition, onFinished, sender,
); );
const displayName = OwnProfileStore.instance.displayName; const displayName = OwnProfileStore.instance.displayName;
const userId = matrixClient.getSafeUserId();
const onLocationSubmit = const onLocationSubmit =
shareType === LocationShareType.Live shareType === LocationShareType.Live
? shareLiveLocation(matrixClient, roomId, displayName, openMenu) ? shareLiveLocation(matrixClient, roomId, displayName || userId, openMenu)
: shareLocation(matrixClient, roomId, shareType, relation, openMenu); : shareLocation(matrixClient, roomId, shareType ?? LocationShareType.Own, relation, openMenu);
const onLiveShareEnableSubmit = (): void => { const onLiveShareEnableSubmit = (): void => {
SettingsStore.setValue("feature_location_share_live", undefined, SettingLevel.DEVICE, true); SettingsStore.setValue("feature_location_share_live", null, SettingLevel.DEVICE, true);
}; };
const shouldAdvertiseLiveLabsFlag = shareType === LocationShareType.Live && !isLiveShareEnabled; const shouldAdvertiseLiveLabsFlag = shareType === LocationShareType.Live && !isLiveShareEnabled;

View File

@ -31,7 +31,7 @@ interface IProps extends IDialogProps {
} }
interface IState { interface IState {
error: Error; error?: Error;
} }
/** /**
@ -58,7 +58,7 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
const { mxEvent } = this.props; const { mxEvent } = this.props;
// only pass member to marker when should render avatar marker // only pass member to marker when should render avatar marker
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined; const markerRoomMember = (isSelfLocation(mxEvent.getContent()) && mxEvent.sender) || undefined;
const geoUri = locationEventGeoUri(mxEvent); const geoUri = locationEventGeoUri(mxEvent);
return ( return (
<BaseDialog className="mx_LocationViewDialog" onFinished={this.props.onFinished} fixedWidth={false}> <BaseDialog className="mx_LocationViewDialog" onFinished={this.props.onFinished} fixedWidth={false}>

View File

@ -70,6 +70,9 @@ const useMapWithStyle = ({
if (map && centerGeoUri) { if (map && centerGeoUri) {
try { try {
const coords = parseGeoUri(centerGeoUri); const coords = parseGeoUri(centerGeoUri);
if (!coords) {
throw new Error("Invalid geo URI");
}
map.setCenter({ lon: coords.longitude, lat: coords.latitude }); map.setCenter({ lon: coords.longitude, lat: coords.latitude });
} catch (_error) { } catch (_error) {
logger.error("Could not set map center"); logger.error("Could not set map center");

View File

@ -20,7 +20,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { OwnProfileStore } from "../../../stores/OwnProfileStore"; import { OwnProfileStore } from "../../../stores/OwnProfileStore";
import BaseAvatar from "../avatars/BaseAvatar"; import BaseAvatar from "../avatars/BaseAvatar";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import Heading from "../typography/Heading"; import Heading from "../typography/Heading";
import { Icon as LocationIcon } from "../../../../res/img/element-icons/location.svg"; import { Icon as LocationIcon } from "../../../../res/img/element-icons/location.svg";
import { LocationShareType } from "./shareLocation"; import { LocationShareType } from "./shareLocation";
@ -28,11 +28,11 @@ import StyledLiveBeaconIcon from "../beacon/StyledLiveBeaconIcon";
const UserAvatar: React.FC = () => { const UserAvatar: React.FC = () => {
const matrixClient = useContext(MatrixClientContext); const matrixClient = useContext(MatrixClientContext);
const userId = matrixClient.getUserId(); const userId = matrixClient.getSafeUserId();
const displayName = OwnProfileStore.instance.displayName; const displayName = OwnProfileStore.instance.displayName ?? undefined;
// 40 - 2px border // 40 - 2px border
const avatarSize = 36; const avatarSize = 36;
const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize) ?? undefined;
return ( return (
<div className={`mx_ShareType_option-icon ${LocationShareType.Own}`}> <div className={`mx_ShareType_option-icon ${LocationShareType.Own}`}>
@ -49,9 +49,13 @@ const UserAvatar: React.FC = () => {
); );
}; };
type ShareTypeOptionProps = HTMLAttributes<Element> & { label: string; shareType: LocationShareType }; type ShareTypeOptionProps = HTMLAttributes<Element> & {
label: string;
shareType: LocationShareType;
onClick?: ((e: ButtonEvent) => void | Promise<void>) | null;
};
const ShareTypeOption: React.FC<ShareTypeOptionProps> = ({ onClick, label, shareType, ...rest }) => ( const ShareTypeOption: React.FC<ShareTypeOptionProps> = ({ onClick, label, shareType, ...rest }) => (
<AccessibleButton element="button" className="mx_ShareType_option" onClick={onClick} {...rest}> <AccessibleButton element="button" className="mx_ShareType_option" onClick={onClick ?? null} {...rest}>
{shareType === LocationShareType.Own && <UserAvatar />} {shareType === LocationShareType.Own && <UserAvatar />}
{shareType === LocationShareType.Pin && ( {shareType === LocationShareType.Pin && (
<LocationIcon className={`mx_ShareType_option-icon ${LocationShareType.Pin}`} /> <LocationIcon className={`mx_ShareType_option-icon ${LocationShareType.Pin}`} />

View File

@ -33,9 +33,11 @@ const useMapMarker = (
return; return;
} }
const coords = parseGeoUri(geoUri); const coords = parseGeoUri(geoUri);
const newMarker = createMarker(coords, element); if (coords) {
newMarker.addTo(map); const newMarker = createMarker(coords, element);
setMarker(newMarker); newMarker.addTo(map);
setMarker(newMarker);
}
}, },
[marker, geoUri, map], [marker, geoUri, map],
); );
@ -43,7 +45,9 @@ const useMapMarker = (
useEffect(() => { useEffect(() => {
if (marker) { if (marker) {
const coords = parseGeoUri(geoUri); const coords = parseGeoUri(geoUri);
marker.setLngLat({ lon: coords.longitude, lat: coords.latitude }); if (coords) {
marker.setLngLat({ lon: coords.longitude, lat: coords.latitude });
}
} }
}, [marker, geoUri]); }, [marker, geoUri]);

View File

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { IContent } from "matrix-js-sdk/src/matrix";
import { MatrixError } from "matrix-js-sdk/src/http-api"; import { MatrixError } from "matrix-js-sdk/src/http-api";
import { makeLocationContent, makeBeaconInfoContent } from "matrix-js-sdk/src/content-helpers"; import { makeLocationContent, makeBeaconInfoContent } from "matrix-js-sdk/src/content-helpers";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
@ -94,7 +95,7 @@ const getDefaultErrorParams = (
return { modalParams, errorMessage }; return { modalParams, errorMessage };
}; };
const handleShareError = (error: Error, openMenu: () => void, shareType: LocationShareType): void => { const handleShareError = (error: unknown, openMenu: () => void, shareType: LocationShareType): void => {
const { modalParams, errorMessage } = const { modalParams, errorMessage } =
(error as MatrixError).errcode === "M_FORBIDDEN" (error as MatrixError).errcode === "M_FORBIDDEN"
? getPermissionsErrorParams(shareType) ? getPermissionsErrorParams(shareType)
@ -135,9 +136,9 @@ export const shareLocation =
async ({ uri, timestamp }): Promise<void> => { async ({ uri, timestamp }): Promise<void> => {
if (!uri) return; if (!uri) return;
try { try {
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null; const threadId = (relation?.rel_type === THREAD_RELATION_TYPE.name && relation?.event_id) || null;
const assetType = shareType === LocationShareType.Pin ? LocationAssetType.Pin : LocationAssetType.Self; const assetType = shareType === LocationShareType.Pin ? LocationAssetType.Pin : LocationAssetType.Self;
const content = makeLocationContent(undefined, uri, timestamp, undefined, assetType); const content = makeLocationContent(undefined, uri, timestamp, undefined, assetType) as IContent;
await doMaybeLocalRoomAction( await doMaybeLocalRoomAction(
roomId, roomId,
(actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content), (actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content),

View File

@ -33,6 +33,7 @@ import { LocationShareType } from "../../../../src/components/views/location/sha
import { import {
flushPromisesWithFakeTimers, flushPromisesWithFakeTimers,
getMockClientWithEventEmitter, getMockClientWithEventEmitter,
mockClientMethodsUser,
setupAsyncStoreWithClient, setupAsyncStoreWithClient,
} from "../../../test-utils"; } from "../../../test-utils";
import Modal from "../../../../src/Modal"; import Modal from "../../../../src/Modal";
@ -74,7 +75,7 @@ jest.mock("../../../../src/Modal", () => ({
describe("<LocationShareMenu />", () => { describe("<LocationShareMenu />", () => {
const userId = "@ernie:server.org"; const userId = "@ernie:server.org";
const mockClient = getMockClientWithEventEmitter({ const mockClient = getMockClientWithEventEmitter({
getUserId: jest.fn().mockReturnValue(userId), ...mockClientMethodsUser(userId),
getClientWellKnown: jest.fn().mockResolvedValue({ getClientWellKnown: jest.fn().mockResolvedValue({
map_style_url: "maps.com", map_style_url: "maps.com",
}), }),
@ -334,7 +335,7 @@ describe("<LocationShareMenu />", () => {
expect(SettingsStore.setValue).toHaveBeenCalledWith( expect(SettingsStore.setValue).toHaveBeenCalledWith(
"feature_location_share_live", "feature_location_share_live",
undefined, null,
SettingLevel.DEVICE, SettingLevel.DEVICE,
true, true,
); );

View File

@ -48,6 +48,7 @@ jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
describe("<SpacePanel />", () => { describe("<SpacePanel />", () => {
const mockClient = { const mockClient = {
getUserId: jest.fn().mockReturnValue("@test:test"), getUserId: jest.fn().mockReturnValue("@test:test"),
getSafeUserId: jest.fn().mockReturnValue("@test:test"),
isGuest: jest.fn(), isGuest: jest.fn(),
getAccountData: jest.fn(), getAccountData: jest.fn(),
} as unknown as MatrixClient; } as unknown as MatrixClient;