Device manager - eagerly create m.local_notification_settings
events (#9353)
* eagerly save m.local_notification_settings events * unskip test * create local notification settings after first non-cached sync
This commit is contained in:
parent
cf029c51dc
commit
c795ada78c
4 changed files with 130 additions and 8 deletions
|
@ -26,6 +26,7 @@ import { M_LOCATION } from "matrix-js-sdk/src/@types/location";
|
|||
import {
|
||||
PermissionChanged as PermissionChangedEvent,
|
||||
} from "@matrix-org/analytics-events/types/typescript/PermissionChanged";
|
||||
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
|
||||
|
||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||
import { PosthogAnalytics } from "./PosthogAnalytics";
|
||||
|
@ -50,6 +51,7 @@ import { localNotificationsAreSilenced } from "./utils/notifications";
|
|||
import { getIncomingCallToastKey, IncomingCallToast } from "./toasts/IncomingCallToast";
|
||||
import ToastStore from "./stores/ToastStore";
|
||||
import { ElementCall } from "./models/Call";
|
||||
import { createLocalNotificationSettingsIfNeeded } from './utils/notifications';
|
||||
|
||||
/*
|
||||
* Dispatches:
|
||||
|
@ -351,12 +353,20 @@ export const Notifier = {
|
|||
return this.toolbarHidden;
|
||||
},
|
||||
|
||||
onSyncStateChange: function(state: string) {
|
||||
if (state === "SYNCING") {
|
||||
onSyncStateChange: function(state: SyncState, prevState?: SyncState, data?: ISyncStateData) {
|
||||
if (state === SyncState.Syncing) {
|
||||
this.isSyncing = true;
|
||||
} else if (state === "STOPPED" || state === "ERROR") {
|
||||
} else if (state === SyncState.Stopped || state === SyncState.Error) {
|
||||
this.isSyncing = false;
|
||||
}
|
||||
|
||||
// wait for first non-cached sync to complete
|
||||
if (
|
||||
![SyncState.Stopped, SyncState.Error].includes(state) &&
|
||||
!data?.fromCache
|
||||
) {
|
||||
createLocalNotificationSettingsIfNeeded(MatrixClientPeg.get());
|
||||
}
|
||||
},
|
||||
|
||||
onEvent: function(ev: MatrixEvent) {
|
||||
|
|
|
@ -14,14 +14,40 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { LOCAL_NOTIFICATION_SETTINGS_PREFIX } from "matrix-js-sdk/src/@types/event";
|
||||
import { LocalNotificationSettings } from "matrix-js-sdk/src/@types/local_notifications";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
export const deviceNotificationSettingsKeys = [
|
||||
"notificationsEnabled",
|
||||
"notificationBodyEnabled",
|
||||
"audioNotificationsEnabled",
|
||||
];
|
||||
|
||||
export function getLocalNotificationAccountDataEventType(deviceId: string): string {
|
||||
return `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}`;
|
||||
}
|
||||
|
||||
export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient): Promise<void> {
|
||||
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
|
||||
const event = cli.getAccountData(eventType);
|
||||
// New sessions will create an account data event to signify they support
|
||||
// remote toggling of push notifications on this device. Default `is_silenced=true`
|
||||
// For backwards compat purposes, older sessions will need to check settings value
|
||||
// to determine what the state of `is_silenced`
|
||||
if (!event) {
|
||||
// If any of the above is true, we fall in the "backwards compat" case,
|
||||
// and `is_silenced` will be set to `false`
|
||||
const isSilenced = !deviceNotificationSettingsKeys.some(key => SettingsStore.getValue(key));
|
||||
|
||||
await cli.setAccountData(eventType, {
|
||||
is_silenced: isSilenced,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function localNotificationsAreSilenced(cli: MatrixClient): boolean {
|
||||
const eventType = getLocalNotificationAccountDataEventType(cli.deviceId);
|
||||
const event = cli.getAccountData(eventType);
|
||||
|
|
|
@ -14,20 +14,30 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MockedObject } from "jest-mock";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { SyncState } from "matrix-js-sdk/src/sync";
|
||||
|
||||
import BasePlatform from "../src/BasePlatform";
|
||||
import { ElementCall } from "../src/models/Call";
|
||||
import Notifier from "../src/Notifier";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
import ToastStore from "../src/stores/ToastStore";
|
||||
import { getLocalNotificationAccountDataEventType } from "../src/utils/notifications";
|
||||
import {
|
||||
createLocalNotificationSettingsIfNeeded,
|
||||
getLocalNotificationAccountDataEventType,
|
||||
} from "../src/utils/notifications";
|
||||
import { getMockClientWithEventEmitter, mkEvent, mkRoom, mockPlatformPeg } from "./test-utils";
|
||||
import { IncomingCallToast } from "../src/toasts/IncomingCallToast";
|
||||
|
||||
jest.mock("../src/utils/notifications", () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual("../src/utils/notifications"),
|
||||
createLocalNotificationSettingsIfNeeded: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("Notifier", () => {
|
||||
const roomId = "!room1:server";
|
||||
const testEvent = mkEvent({
|
||||
|
@ -111,7 +121,7 @@ describe("Notifier", () => {
|
|||
tweaks: {},
|
||||
});
|
||||
|
||||
Notifier.onSyncStateChange("SYNCING");
|
||||
Notifier.onSyncStateChange(SyncState.Syncing);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -169,4 +179,46 @@ describe("Notifier", () => {
|
|||
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('local notification settings', () => {
|
||||
const createLocalNotificationSettingsIfNeededMock = mocked(createLocalNotificationSettingsIfNeeded);
|
||||
let hasStartedNotiferBefore = false;
|
||||
beforeEach(() => {
|
||||
// notifier defines some listener functions in start
|
||||
// and references them in stop
|
||||
// so blows up if stopped before it was started
|
||||
if (hasStartedNotiferBefore) {
|
||||
Notifier.stop();
|
||||
}
|
||||
Notifier.start();
|
||||
hasStartedNotiferBefore = true;
|
||||
createLocalNotificationSettingsIfNeededMock.mockClear();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Notifier.stop();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after a sync error', () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Error, SyncState.Syncing);
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after sync stops', () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Stopped, SyncState.Syncing);
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create local notifications event after a cached sync', () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Syncing, {
|
||||
fromCache: true,
|
||||
});
|
||||
expect(createLocalNotificationSettingsIfNeededMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates local notifications event after a non-cached sync', () => {
|
||||
mockClient.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Syncing, {});
|
||||
expect(createLocalNotificationSettingsIfNeededMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ import { mocked } from "jest-mock";
|
|||
import {
|
||||
localNotificationsAreSilenced,
|
||||
getLocalNotificationAccountDataEventType,
|
||||
createLocalNotificationSettingsIfNeeded,
|
||||
deviceNotificationSettingsKeys,
|
||||
} from "../../src/utils/notifications";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { getMockClientWithEventEmitter } from "../test-utils/client";
|
||||
|
@ -46,6 +48,38 @@ describe('notifications', () => {
|
|||
mocked(SettingsStore).getValue.mockReturnValue(false);
|
||||
});
|
||||
|
||||
describe('createLocalNotification', () => {
|
||||
it('creates account data event', async () => {
|
||||
await createLocalNotificationSettingsIfNeeded(mockClient);
|
||||
const event = mockClient.getAccountData(accountDataEventKey);
|
||||
expect(event?.getContent().is_silenced).toBe(true);
|
||||
});
|
||||
|
||||
it.each(deviceNotificationSettingsKeys)(
|
||||
'unsilenced for existing sessions when %s setting is truthy',
|
||||
async (settingKey) => {
|
||||
mocked(SettingsStore)
|
||||
.getValue
|
||||
.mockImplementation((key) => {
|
||||
return key === settingKey;
|
||||
});
|
||||
|
||||
await createLocalNotificationSettingsIfNeeded(mockClient);
|
||||
const event = mockClient.getAccountData(accountDataEventKey);
|
||||
expect(event?.getContent().is_silenced).toBe(false);
|
||||
});
|
||||
|
||||
it("does not override an existing account event data", async () => {
|
||||
mockClient.setAccountData(accountDataEventKey, {
|
||||
is_silenced: false,
|
||||
});
|
||||
|
||||
await createLocalNotificationSettingsIfNeeded(mockClient);
|
||||
const event = mockClient.getAccountData(accountDataEventKey);
|
||||
expect(event?.getContent().is_silenced).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('localNotificationsAreSilenced', () => {
|
||||
it('defaults to true when no setting exists', () => {
|
||||
expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
|
||||
|
|
Loading…
Reference in a new issue