Live location share - handle insufficient permissions in location sharing (PSG-610) (#9047)
* handle insufficient permissions in location sharing * reformat ternariespull/28788/head^2
parent
c44c8ba654
commit
bda272dce4
|
@ -23,7 +23,7 @@ import { IDialogProps } from "./IDialogProps";
|
||||||
import BaseDialog from "./BaseDialog";
|
import BaseDialog from "./BaseDialog";
|
||||||
import DialogButtons from "../elements/DialogButtons";
|
import DialogButtons from "../elements/DialogButtons";
|
||||||
|
|
||||||
interface IProps extends IDialogProps {
|
export interface IQuestionDialogProps extends IDialogProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: React.ReactNode;
|
description?: React.ReactNode;
|
||||||
extraButtons?: React.ReactNode;
|
extraButtons?: React.ReactNode;
|
||||||
|
@ -39,8 +39,8 @@ interface IProps extends IDialogProps {
|
||||||
cancelButton?: React.ReactNode;
|
cancelButton?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class QuestionDialog extends React.Component<IProps> {
|
export default class QuestionDialog extends React.Component<IQuestionDialogProps> {
|
||||||
public static defaultProps: Partial<IProps> = {
|
public static defaultProps: Partial<IQuestionDialogProps> = {
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
description: "",
|
||||||
extraButtons: null,
|
extraButtons: null,
|
||||||
|
|
|
@ -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 { 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";
|
||||||
import { IEventRelation } from "matrix-js-sdk/src/models/event";
|
import { IEventRelation } from "matrix-js-sdk/src/models/event";
|
||||||
|
@ -23,7 +24,7 @@ import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
import QuestionDialog, { IQuestionDialogProps } from "../dialogs/QuestionDialog";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import { OwnBeaconStore } from "../../../stores/OwnBeaconStore";
|
import { OwnBeaconStore } from "../../../stores/OwnBeaconStore";
|
||||||
import { doMaybeLocalRoomAction } from "../../../utils/local-room";
|
import { doMaybeLocalRoomAction } from "../../../utils/local-room";
|
||||||
|
@ -45,12 +46,32 @@ const DEFAULT_LIVE_DURATION = 300000;
|
||||||
|
|
||||||
export type ShareLocationFn = (props: LocationShareProps) => Promise<void>;
|
export type ShareLocationFn = (props: LocationShareProps) => Promise<void>;
|
||||||
|
|
||||||
const handleShareError = (error: Error, openMenu: () => void, shareType: LocationShareType) => {
|
const getPermissionsErrorParams = (shareType: LocationShareType): {
|
||||||
const errorMessage = shareType === LocationShareType.Live ?
|
errorMessage: string;
|
||||||
"We couldn't start sharing your live location" :
|
modalParams: IQuestionDialogProps;
|
||||||
"We couldn't send your location";
|
} => {
|
||||||
logger.error(errorMessage, error);
|
const errorMessage = shareType === LocationShareType.Live
|
||||||
const params = {
|
? "Insufficient permissions to start sharing your live location"
|
||||||
|
: "Insufficient permissions to send your location";
|
||||||
|
|
||||||
|
const modalParams = {
|
||||||
|
title: _t("You don't have permission to share locations"),
|
||||||
|
description: _t("You need to have the right permissions in order to share locations in this room."),
|
||||||
|
button: _t("OK"),
|
||||||
|
hasCancelButton: false,
|
||||||
|
onFinished: () => {}, // NOOP
|
||||||
|
};
|
||||||
|
return { modalParams, errorMessage };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultErrorParams = (shareType: LocationShareType, openMenu: () => void): {
|
||||||
|
errorMessage: string;
|
||||||
|
modalParams: IQuestionDialogProps;
|
||||||
|
} => {
|
||||||
|
const errorMessage = shareType === LocationShareType.Live
|
||||||
|
? "We couldn't start sharing your live location"
|
||||||
|
: "We couldn't send your location";
|
||||||
|
const modalParams = {
|
||||||
title: _t("We couldn't send your location"),
|
title: _t("We couldn't send your location"),
|
||||||
description: _t("%(brand)s could not send your location. Please try again later.", {
|
description: _t("%(brand)s could not send your location. Please try again later.", {
|
||||||
brand: SdkConfig.get().brand,
|
brand: SdkConfig.get().brand,
|
||||||
|
@ -63,7 +84,17 @@ const handleShareError = (error: Error, openMenu: () => void, shareType: Locatio
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Modal.createDialog(QuestionDialog, params);
|
return { modalParams, errorMessage };
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleShareError = (error: Error, openMenu: () => void, shareType: LocationShareType): void => {
|
||||||
|
const { modalParams, errorMessage } = (error as MatrixError).errcode === 'M_FORBIDDEN' ?
|
||||||
|
getPermissionsErrorParams(shareType) :
|
||||||
|
getDefaultErrorParams(shareType, openMenu);
|
||||||
|
|
||||||
|
logger.error(errorMessage, error);
|
||||||
|
|
||||||
|
Modal.createDialog(QuestionDialog, modalParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shareLiveLocation = (
|
export const shareLiveLocation = (
|
||||||
|
|
|
@ -2199,6 +2199,8 @@
|
||||||
"Failed to fetch your location. Please try again later.": "Failed to fetch your location. Please try again later.",
|
"Failed to fetch your location. Please try again later.": "Failed to fetch your location. Please try again later.",
|
||||||
"Timed out trying to fetch your location. Please try again later.": "Timed out trying to fetch your location. Please try again later.",
|
"Timed out trying to fetch your location. Please try again later.": "Timed out trying to fetch your location. Please try again later.",
|
||||||
"Unknown error fetching location. Please try again later.": "Unknown error fetching location. Please try again later.",
|
"Unknown error fetching location. Please try again later.": "Unknown error fetching location. Please try again later.",
|
||||||
|
"You don't have permission to share locations": "You don't have permission to share locations",
|
||||||
|
"You need to have the right permissions in order to share locations in this room.": "You need to have the right permissions in order to share locations in this room.",
|
||||||
"We couldn't send your location": "We couldn't send your location",
|
"We couldn't send your location": "We couldn't send your location",
|
||||||
"%(brand)s could not send your location. Please try again later.": "%(brand)s could not send your location. Please try again later.",
|
"%(brand)s could not send your location. Please try again later.": "%(brand)s could not send your location. Please try again later.",
|
||||||
"%(displayName)s's live location": "%(displayName)s's live location",
|
"%(displayName)s's live location": "%(displayName)s's live location",
|
||||||
|
|
|
@ -41,6 +41,7 @@ import Modal from '../../../../src/Modal';
|
||||||
import { DEFAULT_DURATION_MS } from '../../../../src/components/views/location/LiveDurationDropdown';
|
import { DEFAULT_DURATION_MS } from '../../../../src/components/views/location/LiveDurationDropdown';
|
||||||
import { OwnBeaconStore } from '../../../../src/stores/OwnBeaconStore';
|
import { OwnBeaconStore } from '../../../../src/stores/OwnBeaconStore';
|
||||||
import { SettingLevel } from '../../../../src/settings/SettingLevel';
|
import { SettingLevel } from '../../../../src/settings/SettingLevel';
|
||||||
|
import QuestionDialog from '../../../../src/components/views/dialogs/QuestionDialog';
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
@ -417,7 +418,7 @@ describe('<LocationShareMenu />', () => {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens error dialog when beacon creation fails ', async () => {
|
it('opens error dialog when beacon creation fails', async () => {
|
||||||
// stub logger to keep console clean from expected error
|
// stub logger to keep console clean from expected error
|
||||||
const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||||
const error = new Error('oh no');
|
const error = new Error('oh no');
|
||||||
|
@ -438,7 +439,41 @@ describe('<LocationShareMenu />', () => {
|
||||||
await flushPromisesWithFakeTimers();
|
await flushPromisesWithFakeTimers();
|
||||||
|
|
||||||
expect(logSpy).toHaveBeenCalledWith("We couldn't start sharing your live location", error);
|
expect(logSpy).toHaveBeenCalledWith("We couldn't start sharing your live location", error);
|
||||||
expect(mocked(Modal).createDialog).toHaveBeenCalled();
|
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({
|
||||||
|
button: 'Try again',
|
||||||
|
description: 'Element could not send your location. Please try again later.',
|
||||||
|
title: `We couldn't send your location`,
|
||||||
|
cancelButton: 'Cancel',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens error dialog when beacon creation fails with permission error', async () => {
|
||||||
|
// stub logger to keep console clean from expected error
|
||||||
|
const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined);
|
||||||
|
const error = { errcode: 'M_FORBIDDEN' } as unknown as Error;
|
||||||
|
mockClient.unstable_createLiveBeacon.mockRejectedValue(error);
|
||||||
|
const component = getComponent();
|
||||||
|
|
||||||
|
// advance to location picker
|
||||||
|
setShareType(component, LocationShareType.Live);
|
||||||
|
setLocation(component);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
getSubmitButton(component).at(0).simulate('click');
|
||||||
|
component.setProps({});
|
||||||
|
});
|
||||||
|
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith("Insufficient permissions to start sharing your live location", error);
|
||||||
|
expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({
|
||||||
|
button: 'OK',
|
||||||
|
description: 'You need to have the right permissions in order to share locations in this room.',
|
||||||
|
title: `You don't have permission to share locations`,
|
||||||
|
hasCancelButton: false,
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue