Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Device manager - eagerly create m.local_notification_settings events #9353

Merged
merged 10 commits into from
Oct 10, 2022
13 changes: 13 additions & 0 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ import { TimelineRenderingType } from "../../contexts/RoomContext";
import { UseCaseSelection } from '../views/elements/UseCaseSelection';
import { ValidatedServerConfig } from '../../utils/ValidatedServerConfig';
import { isLocalRoom } from '../../utils/localRoom/isLocalRoom';
import { createLocalNotificationSettingsIfNeeded } from '../../utils/notifications';

// legacy export
export { default as Views } from "../../Views";
Expand Down Expand Up @@ -350,6 +351,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
initSentry(SdkConfig.get("sentry"));
}

private get cli(): MatrixClient { return MatrixClientPeg.get(); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private get cli(): MatrixClient { return MatrixClientPeg.get(); }
private get cli(): MatrixClient {
return MatrixClientPeg.get();
}


private async postLoginSetup() {
const cli = MatrixClientPeg.get();
const cryptoEnabled = cli.isCryptoEnabled();
Expand Down Expand Up @@ -1242,6 +1245,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.themeWatcher.recheck();
StorageManager.tryPersistStorage();

this.cli.on(ClientEvent.Sync, this.onInitialSync);

if (
MatrixClientPeg.currentUserIsJustRegistered() &&
SettingsStore.getValue("FTUE.useCaseSelection") === null
Expand All @@ -1268,6 +1273,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
}

private onInitialSync = (): void => {
if (this.cli.isInitialSyncComplete()) {
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
this.cli.off(ClientEvent.Sync, this.onInitialSync);
}

createLocalNotificationSettingsIfNeeded(this.cli);
};

private async onShowPostLoginScreen(useCase?: UseCase) {
if (useCase) {
PosthogAnalytics.instance.setProperty("ftueUseCaseSelection", useCase);
Expand Down
28 changes: 27 additions & 1 deletion src/utils/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
34 changes: 34 additions & 0 deletions test/utils/notifications-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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();
Expand Down