Apply strictNullChecks to src/components/views/location/* (#10249
* strict fixes * accessiblebutton without onClick explicit * strict fix for UserMenu BaseAvatarpull/28788/head^2
parent
6de8d85f7e
commit
dd6fc124d7
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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}`} />
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue