Display push toggle for web sessions (MSC3890) (#9327)

This commit is contained in:
Germain 2022-09-28 18:18:10 +01:00 committed by GitHub
parent e15ef9f3de
commit c3bfb6e4a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 166 additions and 33 deletions

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { LocalNotificationSettings } from 'matrix-js-sdk/src/@types/local_notifications';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
@ -29,6 +30,8 @@ interface Props {
device?: DeviceWithVerification; device?: DeviceWithVerification;
isLoading: boolean; isLoading: boolean;
isSigningOut: boolean; isSigningOut: boolean;
localNotificationSettings?: LocalNotificationSettings | undefined;
setPushNotifications?: (deviceId: string, enabled: boolean) => Promise<void> | undefined;
onVerifyCurrentDevice: () => void; onVerifyCurrentDevice: () => void;
onSignOutCurrentDevice: () => void; onSignOutCurrentDevice: () => void;
saveDeviceName: (deviceName: string) => Promise<void>; saveDeviceName: (deviceName: string) => Promise<void>;
@ -38,6 +41,8 @@ const CurrentDeviceSection: React.FC<Props> = ({
device, device,
isLoading, isLoading,
isSigningOut, isSigningOut,
localNotificationSettings,
setPushNotifications,
onVerifyCurrentDevice, onVerifyCurrentDevice,
onSignOutCurrentDevice, onSignOutCurrentDevice,
saveDeviceName, saveDeviceName,
@ -63,6 +68,8 @@ const CurrentDeviceSection: React.FC<Props> = ({
{ isExpanded && { isExpanded &&
<DeviceDetails <DeviceDetails
device={device} device={device}
localNotificationSettings={localNotificationSettings}
setPushNotifications={setPushNotifications}
isSigningOut={isSigningOut} isSigningOut={isSigningOut}
onVerifyDevice={onVerifyCurrentDevice} onVerifyDevice={onVerifyCurrentDevice}
onSignOutDevice={onSignOutCurrentDevice} onSignOutDevice={onSignOutCurrentDevice}

View file

@ -17,6 +17,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { IPusher } from 'matrix-js-sdk/src/@types/PushRules'; import { IPusher } from 'matrix-js-sdk/src/@types/PushRules';
import { PUSHER_ENABLED } from 'matrix-js-sdk/src/@types/event'; import { PUSHER_ENABLED } from 'matrix-js-sdk/src/@types/event';
import { LocalNotificationSettings } from 'matrix-js-sdk/src/@types/local_notifications';
import { formatDate } from '../../../../DateUtils'; import { formatDate } from '../../../../DateUtils';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
@ -30,11 +31,12 @@ import { DeviceWithVerification } from './types';
interface Props { interface Props {
device: DeviceWithVerification; device: DeviceWithVerification;
pusher?: IPusher | undefined; pusher?: IPusher | undefined;
localNotificationSettings?: LocalNotificationSettings | undefined;
isSigningOut: boolean; isSigningOut: boolean;
onVerifyDevice?: () => void; onVerifyDevice?: () => void;
onSignOutDevice: () => void; onSignOutDevice: () => void;
saveDeviceName: (deviceName: string) => Promise<void>; saveDeviceName: (deviceName: string) => Promise<void>;
setPusherEnabled?: (deviceId: string, enabled: boolean) => Promise<void> | undefined; setPushNotifications?: (deviceId: string, enabled: boolean) => Promise<void> | undefined;
supportsMSC3881?: boolean | undefined; supportsMSC3881?: boolean | undefined;
} }
@ -46,11 +48,12 @@ interface MetadataTable {
const DeviceDetails: React.FC<Props> = ({ const DeviceDetails: React.FC<Props> = ({
device, device,
pusher, pusher,
localNotificationSettings,
isSigningOut, isSigningOut,
onVerifyDevice, onVerifyDevice,
onSignOutDevice, onSignOutDevice,
saveDeviceName, saveDeviceName,
setPusherEnabled, setPushNotifications,
supportsMSC3881, supportsMSC3881,
}) => { }) => {
const metadata: MetadataTable[] = [ const metadata: MetadataTable[] = [
@ -70,6 +73,21 @@ const DeviceDetails: React.FC<Props> = ({
], ],
}, },
]; ];
const showPushNotificationSection = !!pusher || !!localNotificationSettings;
function isPushNotificationsEnabled(pusher: IPusher, notificationSettings: LocalNotificationSettings): boolean {
if (pusher) return pusher[PUSHER_ENABLED.name];
if (localNotificationSettings) return !localNotificationSettings.is_silenced;
return true;
}
function isCheckboxDisabled(pusher: IPusher, notificationSettings: LocalNotificationSettings): boolean {
if (localNotificationSettings) return false;
if (pusher && !supportsMSC3881) return true;
return false;
}
return <div className='mx_DeviceDetails' data-testid={`device-detail-${device.device_id}`}> return <div className='mx_DeviceDetails' data-testid={`device-detail-${device.device_id}`}>
<section className='mx_DeviceDetails_section'> <section className='mx_DeviceDetails_section'>
<DeviceDetailHeading <DeviceDetailHeading
@ -102,7 +120,7 @@ const DeviceDetails: React.FC<Props> = ({
</table>, </table>,
) } ) }
</section> </section>
{ pusher && ( { showPushNotificationSection && (
<section <section
className='mx_DeviceDetails_section mx_DeviceDetails_pushNotifications' className='mx_DeviceDetails_section mx_DeviceDetails_pushNotifications'
data-testid='device-detail-push-notification' data-testid='device-detail-push-notification'
@ -110,9 +128,9 @@ const DeviceDetails: React.FC<Props> = ({
<ToggleSwitch <ToggleSwitch
// For backwards compatibility, if `enabled` is missing // For backwards compatibility, if `enabled` is missing
// default to `true` // default to `true`
checked={pusher?.[PUSHER_ENABLED.name] ?? true} checked={isPushNotificationsEnabled(pusher, localNotificationSettings)}
disabled={!supportsMSC3881} disabled={isCheckboxDisabled(pusher, localNotificationSettings)}
onChange={(checked) => setPusherEnabled?.(device.device_id, checked)} onChange={checked => setPushNotifications?.(device.device_id, checked)}
aria-label={_t("Toggle push notifications on this session.")} aria-label={_t("Toggle push notifications on this session.")}
data-testid='device-detail-push-notification-checkbox' data-testid='device-detail-push-notification-checkbox'
/> />

View file

@ -17,6 +17,7 @@ limitations under the License.
import React, { ForwardedRef, forwardRef } from 'react'; import React, { ForwardedRef, forwardRef } from 'react';
import { IPusher } from 'matrix-js-sdk/src/@types/PushRules'; import { IPusher } from 'matrix-js-sdk/src/@types/PushRules';
import { PUSHER_DEVICE_ID } from 'matrix-js-sdk/src/@types/event'; import { PUSHER_DEVICE_ID } from 'matrix-js-sdk/src/@types/event';
import { LocalNotificationSettings } from 'matrix-js-sdk/src/@types/local_notifications';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
import AccessibleButton from '../../elements/AccessibleButton'; import AccessibleButton from '../../elements/AccessibleButton';
@ -39,6 +40,7 @@ import { DevicesState } from './useOwnDevices';
interface Props { interface Props {
devices: DevicesDictionary; devices: DevicesDictionary;
pushers: IPusher[]; pushers: IPusher[];
localNotificationSettings: Map<string, LocalNotificationSettings>;
expandedDeviceIds: DeviceWithVerification['device_id'][]; expandedDeviceIds: DeviceWithVerification['device_id'][];
signingOutDeviceIds: DeviceWithVerification['device_id'][]; signingOutDeviceIds: DeviceWithVerification['device_id'][];
filter?: DeviceSecurityVariation; filter?: DeviceSecurityVariation;
@ -47,7 +49,7 @@ interface Props {
onSignOutDevices: (deviceIds: DeviceWithVerification['device_id'][]) => void; onSignOutDevices: (deviceIds: DeviceWithVerification['device_id'][]) => void;
saveDeviceName: DevicesState['saveDeviceName']; saveDeviceName: DevicesState['saveDeviceName'];
onRequestDeviceVerification?: (deviceId: DeviceWithVerification['device_id']) => void; onRequestDeviceVerification?: (deviceId: DeviceWithVerification['device_id']) => void;
setPusherEnabled: (deviceId: string, enabled: boolean) => Promise<void>; setPushNotifications: (deviceId: string, enabled: boolean) => Promise<void>;
supportsMSC3881?: boolean | undefined; supportsMSC3881?: boolean | undefined;
} }
@ -141,24 +143,26 @@ const NoResults: React.FC<NoResultsProps> = ({ filter, clearFilter }) =>
const DeviceListItem: React.FC<{ const DeviceListItem: React.FC<{
device: DeviceWithVerification; device: DeviceWithVerification;
pusher?: IPusher | undefined; pusher?: IPusher | undefined;
localNotificationSettings?: LocalNotificationSettings | undefined;
isExpanded: boolean; isExpanded: boolean;
isSigningOut: boolean; isSigningOut: boolean;
onDeviceExpandToggle: () => void; onDeviceExpandToggle: () => void;
onSignOutDevice: () => void; onSignOutDevice: () => void;
saveDeviceName: (deviceName: string) => Promise<void>; saveDeviceName: (deviceName: string) => Promise<void>;
onRequestDeviceVerification?: () => void; onRequestDeviceVerification?: () => void;
setPusherEnabled: (deviceId: string, enabled: boolean) => Promise<void>; setPushNotifications: (deviceId: string, enabled: boolean) => Promise<void>;
supportsMSC3881?: boolean | undefined; supportsMSC3881?: boolean | undefined;
}> = ({ }> = ({
device, device,
pusher, pusher,
localNotificationSettings,
isExpanded, isExpanded,
isSigningOut, isSigningOut,
onDeviceExpandToggle, onDeviceExpandToggle,
onSignOutDevice, onSignOutDevice,
saveDeviceName, saveDeviceName,
onRequestDeviceVerification, onRequestDeviceVerification,
setPusherEnabled, setPushNotifications,
supportsMSC3881, supportsMSC3881,
}) => <li className='mx_FilteredDeviceList_listItem'> }) => <li className='mx_FilteredDeviceList_listItem'>
<DeviceTile <DeviceTile
@ -174,11 +178,12 @@ const DeviceListItem: React.FC<{
<DeviceDetails <DeviceDetails
device={device} device={device}
pusher={pusher} pusher={pusher}
localNotificationSettings={localNotificationSettings}
isSigningOut={isSigningOut} isSigningOut={isSigningOut}
onVerifyDevice={onRequestDeviceVerification} onVerifyDevice={onRequestDeviceVerification}
onSignOutDevice={onSignOutDevice} onSignOutDevice={onSignOutDevice}
saveDeviceName={saveDeviceName} saveDeviceName={saveDeviceName}
setPusherEnabled={setPusherEnabled} setPushNotifications={setPushNotifications}
supportsMSC3881={supportsMSC3881} supportsMSC3881={supportsMSC3881}
/> />
} }
@ -192,6 +197,7 @@ export const FilteredDeviceList =
forwardRef(({ forwardRef(({
devices, devices,
pushers, pushers,
localNotificationSettings,
filter, filter,
expandedDeviceIds, expandedDeviceIds,
signingOutDeviceIds, signingOutDeviceIds,
@ -200,7 +206,7 @@ export const FilteredDeviceList =
saveDeviceName, saveDeviceName,
onSignOutDevices, onSignOutDevices,
onRequestDeviceVerification, onRequestDeviceVerification,
setPusherEnabled, setPushNotifications,
supportsMSC3881, supportsMSC3881,
}: Props, ref: ForwardedRef<HTMLDivElement>) => { }: Props, ref: ForwardedRef<HTMLDivElement>) => {
const sortedDevices = getFilteredSortedDevices(devices, filter); const sortedDevices = getFilteredSortedDevices(devices, filter);
@ -258,6 +264,7 @@ export const FilteredDeviceList =
key={device.device_id} key={device.device_id}
device={device} device={device}
pusher={getPusherForDevice(device)} pusher={getPusherForDevice(device)}
localNotificationSettings={localNotificationSettings.get(device.device_id)}
isExpanded={expandedDeviceIds.includes(device.device_id)} isExpanded={expandedDeviceIds.includes(device.device_id)}
isSigningOut={signingOutDeviceIds.includes(device.device_id)} isSigningOut={signingOutDeviceIds.includes(device.device_id)}
onDeviceExpandToggle={() => onDeviceExpandToggle(device.device_id)} onDeviceExpandToggle={() => onDeviceExpandToggle(device.device_id)}
@ -268,7 +275,7 @@ export const FilteredDeviceList =
? () => onRequestDeviceVerification(device.device_id) ? () => onRequestDeviceVerification(device.device_id)
: undefined : undefined
} }
setPusherEnabled={setPusherEnabled} setPushNotifications={setPushNotifications}
supportsMSC3881={supportsMSC3881} supportsMSC3881={supportsMSC3881}
/>, />,
) } ) }

View file

@ -15,11 +15,19 @@ limitations under the License.
*/ */
import { useCallback, useContext, useEffect, useState } from "react"; import { useCallback, useContext, useEffect, useState } from "react";
import { IMyDevice, IPusher, MatrixClient, PUSHER_DEVICE_ID, PUSHER_ENABLED } from "matrix-js-sdk/src/matrix"; import {
IMyDevice,
IPusher,
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
MatrixClient,
PUSHER_DEVICE_ID,
PUSHER_ENABLED,
} from "matrix-js-sdk/src/matrix";
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning"; import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { MatrixError } from "matrix-js-sdk/src/http-api"; import { MatrixError } from "matrix-js-sdk/src/http-api";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifications";
import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../../contexts/MatrixClientContext";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
@ -77,13 +85,14 @@ export enum OwnDevicesError {
export type DevicesState = { export type DevicesState = {
devices: DevicesDictionary; devices: DevicesDictionary;
pushers: IPusher[]; pushers: IPusher[];
localNotificationSettings: Map<string, LocalNotificationSettings>;
currentDeviceId: string; currentDeviceId: string;
isLoadingDeviceList: boolean; isLoadingDeviceList: boolean;
// not provided when current session cannot request verification // not provided when current session cannot request verification
requestDeviceVerification?: (deviceId: DeviceWithVerification['device_id']) => Promise<VerificationRequest>; requestDeviceVerification?: (deviceId: DeviceWithVerification['device_id']) => Promise<VerificationRequest>;
refreshDevices: () => Promise<void>; refreshDevices: () => Promise<void>;
saveDeviceName: (deviceId: DeviceWithVerification['device_id'], deviceName: string) => Promise<void>; saveDeviceName: (deviceId: DeviceWithVerification['device_id'], deviceName: string) => Promise<void>;
setPusherEnabled: (deviceId: DeviceWithVerification['device_id'], enabled: boolean) => Promise<void>; setPushNotifications: (deviceId: DeviceWithVerification['device_id'], enabled: boolean) => Promise<void>;
error?: OwnDevicesError; error?: OwnDevicesError;
supportsMSC3881?: boolean | undefined; supportsMSC3881?: boolean | undefined;
}; };
@ -95,6 +104,8 @@ export const useOwnDevices = (): DevicesState => {
const [devices, setDevices] = useState<DevicesState['devices']>({}); const [devices, setDevices] = useState<DevicesState['devices']>({});
const [pushers, setPushers] = useState<DevicesState['pushers']>([]); const [pushers, setPushers] = useState<DevicesState['pushers']>([]);
const [localNotificationSettings, setLocalNotificationSettings]
= useState<DevicesState['localNotificationSettings']>(new Map<string, LocalNotificationSettings>());
const [isLoadingDeviceList, setIsLoadingDeviceList] = useState(true); const [isLoadingDeviceList, setIsLoadingDeviceList] = useState(true);
const [supportsMSC3881, setSupportsMSC3881] = useState(true); // optimisticly saying yes! const [supportsMSC3881, setSupportsMSC3881] = useState(true); // optimisticly saying yes!
@ -120,6 +131,19 @@ export const useOwnDevices = (): DevicesState => {
const { pushers } = await matrixClient.getPushers(); const { pushers } = await matrixClient.getPushers();
setPushers(pushers); setPushers(pushers);
const notificationSettings = new Map<string, LocalNotificationSettings>();
Object.keys(devices).forEach((deviceId) => {
const eventType = `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}`;
const event = matrixClient.getAccountData(eventType);
if (event) {
notificationSettings.set(
deviceId,
event.getContent(),
);
}
});
setLocalNotificationSettings(notificationSettings);
setIsLoadingDeviceList(false); setIsLoadingDeviceList(false);
} catch (error) { } catch (error) {
if ((error as MatrixError).httpStatus == 404) { if ((error as MatrixError).httpStatus == 404) {
@ -169,32 +193,40 @@ export const useOwnDevices = (): DevicesState => {
} }
}, [matrixClient, devices, refreshDevices]); }, [matrixClient, devices, refreshDevices]);
const setPusherEnabled = useCallback( const setPushNotifications = useCallback(
async (deviceId: DeviceWithVerification['device_id'], enabled: boolean): Promise<void> => { async (deviceId: DeviceWithVerification['device_id'], enabled: boolean): Promise<void> => {
const pusher = pushers.find(pusher => pusher[PUSHER_DEVICE_ID.name] === deviceId);
try { try {
const pusher = pushers.find(pusher => pusher[PUSHER_DEVICE_ID.name] === deviceId);
if (pusher) {
await matrixClient.setPusher({ await matrixClient.setPusher({
...pusher, ...pusher,
[PUSHER_ENABLED.name]: enabled, [PUSHER_ENABLED.name]: enabled,
}); });
await refreshDevices(); } else if (localNotificationSettings.has(deviceId)) {
await matrixClient.setLocalNotificationSettings(deviceId, {
is_silenced: !enabled,
});
}
} catch (error) { } catch (error) {
logger.error("Error setting pusher state", error); logger.error("Error setting pusher state", error);
throw new Error(_t("Failed to set pusher state")); throw new Error(_t("Failed to set pusher state"));
} finally {
await refreshDevices();
} }
}, [matrixClient, pushers, refreshDevices], }, [matrixClient, pushers, localNotificationSettings, refreshDevices],
); );
return { return {
devices, devices,
pushers, pushers,
localNotificationSettings,
currentDeviceId, currentDeviceId,
isLoadingDeviceList, isLoadingDeviceList,
error, error,
requestDeviceVerification, requestDeviceVerification,
refreshDevices, refreshDevices,
saveDeviceName, saveDeviceName,
setPusherEnabled, setPushNotifications,
supportsMSC3881, supportsMSC3881,
}; };
}; };

View file

@ -88,12 +88,13 @@ const SessionManagerTab: React.FC = () => {
const { const {
devices, devices,
pushers, pushers,
localNotificationSettings,
currentDeviceId, currentDeviceId,
isLoadingDeviceList, isLoadingDeviceList,
requestDeviceVerification, requestDeviceVerification,
refreshDevices, refreshDevices,
saveDeviceName, saveDeviceName,
setPusherEnabled, setPushNotifications,
supportsMSC3881, supportsMSC3881,
} = useOwnDevices(); } = useOwnDevices();
const [filter, setFilter] = useState<DeviceSecurityVariation>(); const [filter, setFilter] = useState<DeviceSecurityVariation>();
@ -171,9 +172,11 @@ const SessionManagerTab: React.FC = () => {
/> />
<CurrentDeviceSection <CurrentDeviceSection
device={currentDevice} device={currentDevice}
isSigningOut={signingOutDeviceIds.includes(currentDevice?.device_id)} localNotificationSettings={localNotificationSettings.get(currentDeviceId)}
setPushNotifications={setPushNotifications}
isSigningOut={signingOutDeviceIds.includes(currentDeviceId)}
isLoading={isLoadingDeviceList} isLoading={isLoadingDeviceList}
saveDeviceName={(deviceName) => saveDeviceName(currentDevice?.device_id, deviceName)} saveDeviceName={(deviceName) => saveDeviceName(currentDeviceId, deviceName)}
onVerifyCurrentDevice={onVerifyCurrentDevice} onVerifyCurrentDevice={onVerifyCurrentDevice}
onSignOutCurrentDevice={onSignOutCurrentDevice} onSignOutCurrentDevice={onSignOutCurrentDevice}
/> />
@ -190,6 +193,7 @@ const SessionManagerTab: React.FC = () => {
<FilteredDeviceList <FilteredDeviceList
devices={otherDevices} devices={otherDevices}
pushers={pushers} pushers={pushers}
localNotificationSettings={localNotificationSettings}
filter={filter} filter={filter}
expandedDeviceIds={expandedDeviceIds} expandedDeviceIds={expandedDeviceIds}
signingOutDeviceIds={signingOutDeviceIds} signingOutDeviceIds={signingOutDeviceIds}
@ -198,7 +202,7 @@ const SessionManagerTab: React.FC = () => {
onRequestDeviceVerification={requestDeviceVerification ? onTriggerDeviceVerification : undefined} onRequestDeviceVerification={requestDeviceVerification ? onTriggerDeviceVerification : undefined}
onSignOutDevices={onSignOutOtherDevices} onSignOutDevices={onSignOutOtherDevices}
saveDeviceName={saveDeviceName} saveDeviceName={saveDeviceName}
setPusherEnabled={setPusherEnabled} setPushNotifications={setPushNotifications}
ref={filteredDeviceListRef} ref={filteredDeviceListRef}
supportsMSC3881={supportsMSC3881} supportsMSC3881={supportsMSC3881}
/> />

View file

@ -33,7 +33,7 @@ describe('<DeviceDetails />', () => {
isLoading: false, isLoading: false,
onSignOutDevice: jest.fn(), onSignOutDevice: jest.fn(),
saveDeviceName: jest.fn(), saveDeviceName: jest.fn(),
setPusherEnabled: jest.fn(), setPushNotifications: jest.fn(),
supportsMSC3881: true, supportsMSC3881: true,
}; };
@ -157,6 +157,27 @@ describe('<DeviceDetails />', () => {
fireEvent.click(checkbox); fireEvent.click(checkbox);
expect(defaultProps.setPusherEnabled).toHaveBeenCalledWith(device.device_id, !enabled); expect(defaultProps.setPushNotifications).toHaveBeenCalledWith(device.device_id, !enabled);
});
it('changes the local notifications settings status when clicked', () => {
const device = {
...baseDevice,
};
const enabled = false;
const { getByTestId } = render(getComponent({
device,
localNotificationSettings: {
is_silenced: !enabled,
},
isSigningOut: true,
}));
const checkbox = getByTestId('device-detail-push-notification-checkbox');
fireEvent.click(checkbox);
expect(defaultProps.setPushNotifications).toHaveBeenCalledWith(device.device_id, !enabled);
}); });
}); });

View file

@ -45,9 +45,10 @@ describe('<FilteredDeviceList />', () => {
onDeviceExpandToggle: jest.fn(), onDeviceExpandToggle: jest.fn(),
onSignOutDevices: jest.fn(), onSignOutDevices: jest.fn(),
saveDeviceName: jest.fn(), saveDeviceName: jest.fn(),
setPusherEnabled: jest.fn(), setPushNotifications: jest.fn(),
expandedDeviceIds: [], expandedDeviceIds: [],
signingOutDeviceIds: [], signingOutDeviceIds: [],
localNotificationSettings: new Map(),
devices: { devices: {
[unverifiedNoMetadata.device_id]: unverifiedNoMetadata, [unverifiedNoMetadata.device_id]: unverifiedNoMetadata,
[verifiedNoMetadata.device_id]: verifiedNoMetadata, [verifiedNoMetadata.device_id]: verifiedNoMetadata,

View file

@ -22,7 +22,13 @@ import { logger } from 'matrix-js-sdk/src/logger';
import { DeviceTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning'; import { DeviceTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning';
import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest'; import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest';
import { sleep } from 'matrix-js-sdk/src/utils'; import { sleep } from 'matrix-js-sdk/src/utils';
import { IMyDevice, PUSHER_DEVICE_ID, PUSHER_ENABLED } from 'matrix-js-sdk/src/matrix'; import {
IMyDevice,
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
MatrixEvent,
PUSHER_DEVICE_ID,
PUSHER_ENABLED,
} from 'matrix-js-sdk/src/matrix';
import SessionManagerTab from '../../../../../../src/components/views/settings/tabs/user/SessionManagerTab'; import SessionManagerTab from '../../../../../../src/components/views/settings/tabs/user/SessionManagerTab';
import MatrixClientContext from '../../../../../../src/contexts/MatrixClientContext'; import MatrixClientContext from '../../../../../../src/contexts/MatrixClientContext';
@ -71,6 +77,8 @@ describe('<SessionManagerTab />', () => {
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(true), doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(true),
getPushers: jest.fn(), getPushers: jest.fn(),
setPusher: jest.fn(), setPusher: jest.fn(),
getAccountData: jest.fn(),
setLocalNotificationSettings: jest.fn(),
}); });
const defaultProps = {}; const defaultProps = {};
@ -114,6 +122,19 @@ describe('<SessionManagerTab />', () => {
[PUSHER_ENABLED.name]: true, [PUSHER_ENABLED.name]: true,
})], })],
}); });
mockClient.getAccountData
.mockReset()
.mockImplementation(eventType => {
if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) {
return new MatrixEvent({
type: eventType,
content: {
is_silenced: false,
},
});
}
});
}); });
it('renders spinner while devices load', () => { it('renders spinner while devices load', () => {
@ -333,7 +354,6 @@ describe('<SessionManagerTab />', () => {
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockCrossSigningInfo.checkDeviceTrust mockCrossSigningInfo.checkDeviceTrust
.mockImplementation((_userId, { deviceId }) => { .mockImplementation((_userId, { deviceId }) => {
console.log('hhh', deviceId);
if (deviceId === alicesDevice.device_id) { if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false); return new DeviceTrustLevel(true, true, false, false);
} }
@ -363,7 +383,6 @@ describe('<SessionManagerTab />', () => {
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockCrossSigningInfo.checkDeviceTrust mockCrossSigningInfo.checkDeviceTrust
.mockImplementation((_userId, { deviceId }) => { .mockImplementation((_userId, { deviceId }) => {
console.log('hhh', deviceId);
if (deviceId === alicesDevice.device_id) { if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false); return new DeviceTrustLevel(true, true, false, false);
} }
@ -702,4 +721,28 @@ describe('<SessionManagerTab />', () => {
expect(mockClient.setPusher).toHaveBeenCalled(); expect(mockClient.setPusher).toHaveBeenCalled();
}); });
it("lets you change the local notification settings state", async () => {
const { getByTestId } = render(getComponent());
await act(async () => {
await flushPromisesWithFakeTimers();
});
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
// device details are expanded
expect(getByTestId(`device-detail-${alicesDevice.device_id}`)).toBeTruthy();
expect(getByTestId('device-detail-push-notification')).toBeTruthy();
const checkbox = getByTestId('device-detail-push-notification-checkbox');
expect(checkbox).toBeTruthy();
fireEvent.click(checkbox);
expect(mockClient.setLocalNotificationSettings).toHaveBeenCalledWith(
alicesDevice.device_id,
{ is_silenced: true },
);
});
}); });