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

Commit

Permalink
Make clear notifications work with threads (#9575)
Browse files Browse the repository at this point in the history
  • Loading branch information
Germain authored and Amy Walker committed Nov 28, 2022
1 parent d78ff4a commit ab52d31
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 237 deletions.
52 changes: 29 additions & 23 deletions src/components/views/settings/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import AccessibleButton from "../elements/AccessibleButton";
import TagComposer from "../elements/TagComposer";
import { objectClone } from "../../../utils/objects";
import { arrayDiff } from "../../../utils/arrays";
import { getLocalNotificationAccountDataEventType } from "../../../utils/notifications";
import { clearAllNotifications, getLocalNotificationAccountDataEventType } from "../../../utils/notifications";

// TODO: this "view" component still has far too much application logic in it,
// which should be factored out to other files.
Expand Down Expand Up @@ -112,6 +112,8 @@ interface IState {
desktopNotifications: boolean;
desktopShowBody: boolean;
audioNotifications: boolean;

clearingNotifications: boolean;
}

export default class Notifications extends React.PureComponent<IProps, IState> {
Expand All @@ -126,6 +128,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
desktopNotifications: SettingsStore.getValue("notificationsEnabled"),
desktopShowBody: SettingsStore.getValue("notificationBodyEnabled"),
audioNotifications: SettingsStore.getValue("audioNotificationsEnabled"),
clearingNotifications: false,
};

this.settingWatchers = [
Expand Down Expand Up @@ -177,8 +180,12 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
])).reduce((p, c) => Object.assign(c, p), {});

this.setState<keyof Omit<IState,
"deviceNotificationsEnabled" | "desktopNotifications" | "desktopShowBody" | "audioNotifications">
>({
"deviceNotificationsEnabled" |
"desktopNotifications" |
"desktopShowBody" |
"audioNotifications" |
"clearingNotifications"
>>({
...newState,
phase: Phase.Ready,
});
Expand Down Expand Up @@ -433,17 +440,14 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
}
};

private onClearNotificationsClicked = () => {
const client = MatrixClientPeg.get();
client.getRooms().forEach(r => {
if (r.getUnreadNotificationCount() > 0) {
const events = r.getLiveTimeline().getEvents();
if (events.length) {
// noinspection JSIgnoredPromiseFromCall
client.sendReadReceipt(events[events.length - 1]);
}
}
});
private onClearNotificationsClicked = async (): Promise<void> => {
try {
this.setState({ clearingNotifications: true });
const client = MatrixClientPeg.get();
await clearAllNotifications(client);
} finally {
this.setState({ clearingNotifications: false });
}
};

private async setKeywords(keywords: string[], originalRules: IAnnotatedPushRule[]) {
Expand Down Expand Up @@ -531,7 +535,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {

private renderTopSection() {
const masterSwitch = <LabelledToggleSwitch
data-test-id='notif-master-switch'
data-testid='notif-master-switch'
value={!this.isInhibited}
label={_t("Enable notifications for this account")}
caption={_t("Turn off to disable notifications on all your devices and sessions")}
Expand All @@ -546,7 +550,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {

const emailSwitches = (this.state.threepids || []).filter(t => t.medium === ThreepidMedium.Email)
.map(e => <LabelledToggleSwitch
data-test-id='notif-email-switch'
data-testid='notif-email-switch'
key={e.address}
value={this.state.pushers.some(p => p.kind === "email" && p.pushkey === e.address)}
label={_t("Enable email notifications for %(email)s", { email: e.address })}
Expand All @@ -558,7 +562,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
{ masterSwitch }

<LabelledToggleSwitch
data-test-id='notif-device-switch'
data-testid='notif-device-switch'
value={this.state.deviceNotificationsEnabled}
label={_t("Enable notifications for this device")}
onChange={checked => this.updateDeviceNotifications(checked)}
Expand All @@ -567,21 +571,21 @@ export default class Notifications extends React.PureComponent<IProps, IState> {

{ this.state.deviceNotificationsEnabled && (<>
<LabelledToggleSwitch
data-test-id='notif-setting-notificationsEnabled'
data-testid='notif-setting-notificationsEnabled'
value={this.state.desktopNotifications}
onChange={this.onDesktopNotificationsChanged}
label={_t('Enable desktop notifications for this session')}
disabled={this.state.phase === Phase.Persisting}
/>
<LabelledToggleSwitch
data-test-id='notif-setting-notificationBodyEnabled'
data-testid='notif-setting-notificationBodyEnabled'
value={this.state.desktopShowBody}
onChange={this.onDesktopShowBodyChanged}
label={_t('Show message in desktop notification')}
disabled={this.state.phase === Phase.Persisting}
/>
<LabelledToggleSwitch
data-test-id='notif-setting-audioNotificationsEnabled'
data-testid='notif-setting-audioNotificationsEnabled'
value={this.state.audioNotifications}
onChange={this.onAudioNotificationsChanged}
label={_t('Enable audible notifications for this session')}
Expand All @@ -605,8 +609,10 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
) {
clearNotifsButton = <AccessibleButton
onClick={this.onClearNotificationsClicked}
disabled={this.state.clearingNotifications}
kind='danger'
className='mx_UserNotifSettings_clearNotifsButton'
data-testid="clear-notifications"
>{ _t("Clear notifications") }</AccessibleButton>;
}

Expand Down Expand Up @@ -653,7 +659,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
const fieldsetRows = this.state.vectorPushRules[category].map(r =>
<fieldset
key={category + r.ruleId}
data-test-id={category + r.ruleId}
data-testid={category + r.ruleId}
className='mx_UserNotifSettings_gridRowContainer'
>
<legend className='mx_UserNotifSettings_gridRowLabel'>{ r.description }</legend>
Expand All @@ -678,7 +684,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
}

return <>
<div data-test-id={`notif-section-${category}`} className='mx_UserNotifSettings_grid'>
<div data-testid={`notif-section-${category}`} className='mx_UserNotifSettings_grid'>
<span className='mx_UserNotifSettings_gridRowLabel mx_UserNotifSettings_gridRowHeading'>{ sectionName }</span>
<span className='mx_UserNotifSettings_gridColumnLabel'>{ VectorStateToLabel[VectorState.Off] }</span>
<span className='mx_UserNotifSettings_gridColumnLabel'>{ VectorStateToLabel[VectorState.On] }</span>
Expand Down Expand Up @@ -715,7 +721,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
// Ends up default centered
return <Spinner />;
} else if (this.state.phase === Phase.Error) {
return <p data-test-id='error-message'>{ _t("There was an error loading your notification settings.") }</p>;
return <p data-testid='error-message'>{ _t("There was an error loading your notification settings.") }</p>;
}

return <div className='mx_UserNotifSettings'>
Expand Down
30 changes: 30 additions & 0 deletions src/utils/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ 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 { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
import { Room } from "matrix-js-sdk/src/models/room";

import SettingsStore from "../settings/SettingsStore";

Expand Down Expand Up @@ -56,3 +58,31 @@ export function localNotificationsAreSilenced(cli: MatrixClient): boolean {
const event = cli.getAccountData(eventType);
return event?.getContent<LocalNotificationSettings>()?.is_silenced ?? false;
}

export function clearAllNotifications(client: MatrixClient): Promise<Array<{}>> {
const receiptPromises = client.getRooms().reduce((promises, room: Room) => {
if (room.getUnreadNotificationCount() > 0) {
const roomEvents = room.getLiveTimeline().getEvents();
const lastThreadEvents = room.lastThread?.events;

const lastRoomEvent = roomEvents?.[roomEvents?.length - 1];
const lastThreadLastEvent = lastThreadEvents?.[lastThreadEvents?.length - 1];

const lastEvent = (lastRoomEvent?.getTs() ?? 0) > (lastThreadLastEvent?.getTs() ?? 0)
? lastRoomEvent
: lastThreadLastEvent;

if (lastEvent) {
const receiptType = SettingsStore.getValue("sendReadReceipts", room.roomId)
? ReceiptType.Read
: ReceiptType.ReadPrivate;
const promise = client.sendReadReceipt(lastEvent, receiptType, true);
promises.push(promise);
}
}

return promises;
}, []);

return Promise.all(receiptPromises);
}
Loading

0 comments on commit ab52d31

Please sign in to comment.