From f14d96e06994b757232f8a58342d2cb548a82180 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 12:36:32 +0100 Subject: [PATCH 01/19] Look in room account data for custom notif sounds --- src/Notifier.js | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 6a4f9827f70..85807976eb9 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -96,11 +96,46 @@ const Notifier = { } }, - _playAudioNotification: function(ev, room) { - const e = document.getElementById("messageAudio"); - if (e) { - e.play(); + _getSoundForRoom: async function(room) { + // We do no caching here because the SDK caches the event content + // and the browser will cache the sound. + const ev = await room.getAccountData("uk.half-shot.notification.sound"); + if (!ev) { + return null; + } + let url = ev.getContent().url; + if (!url) { + console.warn(`${room.roomId} has custom notification sound event, but no url key`); + return null; } + url = MatrixClientPeg.get().mxcUrlToHttp(url); + this.notifSoundsByRoom.set(room.roomId, url); + return url; + }, + + _playAudioNotification: function(ev, room) { + _getSoundForRoom(room).then((soundUrl) => { + console.log(`Got sound ${soundUrl || "default"} for ${room.roomId}`); + // XXX: How do we ensure this is a sound file and not + // going to be exploited? + const selector = document.querySelector(`audio source[src='${soundUrl}']`) || "#messageAudio"; + let audioElement = null; + if (!selector) { + if (!soundUrl) { + console.error("Tried to play alert sound but missing #messageAudio") + return + } + audioElement = new HTMLAudioElement(); + let sourceElement = new HTMLSourceElement(); + // XXX: type + sourceElement.src = soundUrl; + audioElement.appendChild(sourceElement); + document.appendChild(audioElement); + } else { + audioElement = selector.parentNode; + } + audioElement.play(); + }); }, start: function() { From b0bacdba1514c08e2ab0b4dd17a76ebcd189050b Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 13:21:58 +0100 Subject: [PATCH 02/19] Simplyify --- src/Notifier.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 85807976eb9..2caf6116545 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -109,30 +109,23 @@ const Notifier = { return null; } url = MatrixClientPeg.get().mxcUrlToHttp(url); - this.notifSoundsByRoom.set(room.roomId, url); return url; }, _playAudioNotification: function(ev, room) { - _getSoundForRoom(room).then((soundUrl) => { + this._getSoundForRoom(room).then((soundUrl) => { console.log(`Got sound ${soundUrl || "default"} for ${room.roomId}`); // XXX: How do we ensure this is a sound file and not // going to be exploited? - const selector = document.querySelector(`audio source[src='${soundUrl}']`) || "#messageAudio"; - let audioElement = null; + const selector = document.querySelector(soundUrl ? `audio[src='${soundUrl}']` : "#messageAudio"); + let audioElement = selector; if (!selector) { if (!soundUrl) { console.error("Tried to play alert sound but missing #messageAudio") return } - audioElement = new HTMLAudioElement(); - let sourceElement = new HTMLSourceElement(); - // XXX: type - sourceElement.src = soundUrl; - audioElement.appendChild(sourceElement); - document.appendChild(audioElement); - } else { - audioElement = selector.parentNode; + audioElement = new Audio(soundUrl); + document.body.appendChild(audioElement); } audioElement.play(); }); From cd8647188fbd91815b21bf54031f2c16c4baa040 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 14:10:10 +0100 Subject: [PATCH 03/19] Support account level custom sounds too --- src/Notifier.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 2caf6116545..e8d0c27a310 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -99,36 +99,47 @@ const Notifier = { _getSoundForRoom: async function(room) { // We do no caching here because the SDK caches the event content // and the browser will cache the sound. - const ev = await room.getAccountData("uk.half-shot.notification.sound"); + let ev = await room.getAccountData("uk.half-shot.notification.sound"); if (!ev) { - return null; + // Check the account data. + ev = await MatrixClientPeg.get().getAccountData("uk.half-shot.notification.sound"); + if (!ev) { + return null; + } } - let url = ev.getContent().url; - if (!url) { + const content = ev.getContent(); + if (!content.url) { console.warn(`${room.roomId} has custom notification sound event, but no url key`); return null; } - url = MatrixClientPeg.get().mxcUrlToHttp(url); - return url; + return { + url: MatrixClientPeg.get().mxcUrlToHttp(content.url), + type: content.type, + }; }, _playAudioNotification: function(ev, room) { - this._getSoundForRoom(room).then((soundUrl) => { - console.log(`Got sound ${soundUrl || "default"} for ${room.roomId}`); + this._getSoundForRoom(room).then((sound) => { + console.log(`Got sound ${sound || "default"} for ${room.roomId}`); // XXX: How do we ensure this is a sound file and not // going to be exploited? - const selector = document.querySelector(soundUrl ? `audio[src='${soundUrl}']` : "#messageAudio"); + const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); let audioElement = selector; if (!selector) { - if (!soundUrl) { + if (!sound) { console.error("Tried to play alert sound but missing #messageAudio") - return + return; + } + audioElement = new Audio(sound.url); + if (sound.type) { + audioElement.type = sound.type; } - audioElement = new Audio(soundUrl); document.body.appendChild(audioElement); } audioElement.play(); - }); + }).catch((ex) => { + console.warn("Caught error when trying to fetch room notification sound:", ex); + }) }, start: function() { From 63ab7736ca4ab0ee4cfccc8f7b0957b2709e8d2f Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 16:27:30 +0100 Subject: [PATCH 04/19] Add a fancy room tab and uploader --- src/Notifier.js | 19 ++- .../views/dialogs/RoomSettingsDialog.js | 8 +- .../tabs/room/NotificationSettingsTab.js | 139 ++++++++++++++++++ src/i18n/strings/en_EN.json | 6 +- 4 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 src/components/views/settings/tabs/room/NotificationSettingsTab.js diff --git a/src/Notifier.js b/src/Notifier.js index e8d0c27a310..041b91f4b2c 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -96,11 +96,19 @@ const Notifier = { } }, - _getSoundForRoom: async function(room) { + setRoomSound: function(room, soundData) { + return MatrixClientPeg.get().setRoomAccountData(room.roomId, "uk.half-shot.notification.sound", soundData); + }, + + clearRoomSound: function(room) { + return room.setAccountData("uk.half-shot.notification.sound", null); + }, + + getSoundForRoom: async function(room) { // We do no caching here because the SDK caches the event content // and the browser will cache the sound. let ev = await room.getAccountData("uk.half-shot.notification.sound"); - if (!ev) { + if (!ev || !ev.getContent()) { // Check the account data. ev = await MatrixClientPeg.get().getAccountData("uk.half-shot.notification.sound"); if (!ev) { @@ -112,15 +120,18 @@ const Notifier = { console.warn(`${room.roomId} has custom notification sound event, but no url key`); return null; } + return { url: MatrixClientPeg.get().mxcUrlToHttp(content.url), + name: content.name, type: content.type, + size: content.size, }; }, _playAudioNotification: function(ev, room) { - this._getSoundForRoom(room).then((sound) => { - console.log(`Got sound ${sound || "default"} for ${room.roomId}`); + this.getSoundForRoom(room).then((sound) => { + console.log(`Got sound ${sound.name || "default"} for ${room.roomId}`); // XXX: How do we ensure this is a sound file and not // going to be exploited? const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 05ed2620788..733c5002f57 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -22,7 +22,8 @@ import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsT import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab"; import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; -import sdk from "../../../index"; +import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; +import sdk from "../../../index";RolesRoomSettingsTab import MatrixClientPeg from "../../../MatrixClientPeg"; export default class RoomSettingsDialog extends React.Component { @@ -49,6 +50,11 @@ export default class RoomSettingsDialog extends React.Component { "mx_RoomSettingsDialog_rolesIcon", , )); + tabs.push(new Tab( + _td("Notifications"), + "mx_RoomSettingsDialog_rolesIcon", + , + )) tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js new file mode 100644 index 00000000000..35a223a1d85 --- /dev/null +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -0,0 +1,139 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import {_t} from "../../../../../languageHandler"; +import MatrixClientPeg from "../../../../../MatrixClientPeg"; +import sdk from "../../../../.."; +import AccessibleButton from "../../../elements/AccessibleButton"; +import Modal from "../../../../../Modal"; +import dis from "../../../../../dispatcher"; +import Notifier from "../../../../../Notifier"; + +export default class NotificationsSettingsTab extends React.Component { + static propTypes = { + roomId: PropTypes.string.isRequired, + closeSettingsFn: PropTypes.func.isRequired, + }; + + constructor() { + super(); + + this.state = { + currentSound: "default", + uploadedFile: null, + }; + } + + componentWillMount() { + const room = MatrixClientPeg.get().getRoom(this.props.roomId); + Notifier.getSoundForRoom(room).then((soundData) => { + if (!soundData) { + return; + } + this.setState({currentSound: soundData.name || soundData.url}) + }) + } + + _onSoundUploadChanged(e) { + if (!e.target.files || !e.target.files.length) { + this.setState({ + uploadedFile: null, + }); + return; + } + + const file = e.target.files[0]; + this.setState({ + uploadedFile: file, + }); + } + + async _saveSound (e) { + e.stopPropagation(); + e.preventDefault(); + if (!this.state.uploadedFile) { + return; + } + let type = this.state.uploadedFile.type; + if (type === "video/ogg") { + // XXX: I've observed browsers allowing users to pick a audio/ogg files, + // and then calling it a video/ogg. This is a lame hack, but man browsers + // suck at detecting mimetypes. + type = "audio/ogg"; + } + const url = await MatrixClientPeg.get().uploadContent( + this.state.uploadedFile, { + type, + }, + ); + + const room = MatrixClientPeg.get().getRoom(this.props.roomId); + + await Notifier.setRoomSound(room, { + name: this.state.uploadedFile.name, + type: type, + size: this.state.uploadedFile.size, + url, + }); + + this.setState({ + uploadedFile: null, + uploadedFileUrl: null, + currentSound: this.state.uploadedFile.name, + }); + } + + _clearSound (e) { + e.stopPropagation(); + e.preventDefault(); + const room = client.getRoom(this.props.roomId); + Notifier.clearRoomSound(room); + + this.setState({ + currentSound: "default", + }); + } + + render() { + const client = MatrixClientPeg.get(); + + return ( +
+
{_t("Notifications")}
+
+ {_t("Sounds")} +
+ {_t("Notification sound")}: {this.state.currentSound} +
+
+

{_t("Set a new custom sound")}

+
+ + + {_t("Save")} + +
+ + {_t("Reset to default sound")} + +
+
+
+ ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2b50fd9ad32..7f94bdc9cda 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1614,5 +1614,9 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", + "Sounds": "Sounds", + "Notification sound": "Notification sound", + "Set a new custom sound": "Set a new custom sound", + "Reset to default sound": "Reset to default sound" } From d33df45c5e0162a6f681c5007e8b6fc5dc60012f Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 21:42:18 +0100 Subject: [PATCH 05/19] Linting --- src/Notifier.js | 6 +++--- .../views/dialogs/RoomSettingsDialog.js | 4 ++-- .../settings/tabs/room/NotificationSettingsTab.js | 15 +++++---------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 041b91f4b2c..43d599ae0d5 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -120,7 +120,7 @@ const Notifier = { console.warn(`${room.roomId} has custom notification sound event, but no url key`); return null; } - + return { url: MatrixClientPeg.get().mxcUrlToHttp(content.url), name: content.name, @@ -138,7 +138,7 @@ const Notifier = { let audioElement = selector; if (!selector) { if (!sound) { - console.error("Tried to play alert sound but missing #messageAudio") + console.error("Tried to play alert sound but missing #messageAudio"); return; } audioElement = new Audio(sound.url); @@ -150,7 +150,7 @@ const Notifier = { audioElement.play(); }).catch((ex) => { console.warn("Caught error when trying to fetch room notification sound:", ex); - }) + }); }, start: function() { diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 733c5002f57..caed9580031 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -23,7 +23,7 @@ import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab"; import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; -import sdk from "../../../index";RolesRoomSettingsTab +import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; export default class RoomSettingsDialog extends React.Component { @@ -54,7 +54,7 @@ export default class RoomSettingsDialog extends React.Component { _td("Notifications"), "mx_RoomSettingsDialog_rolesIcon", , - )) + )); tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 35a223a1d85..a911ec3e4f9 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -18,10 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../../../languageHandler"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; -import sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; -import Modal from "../../../../../Modal"; -import dis from "../../../../../dispatcher"; import Notifier from "../../../../../Notifier"; export default class NotificationsSettingsTab extends React.Component { @@ -45,8 +42,8 @@ export default class NotificationsSettingsTab extends React.Component { if (!soundData) { return; } - this.setState({currentSound: soundData.name || soundData.url}) - }) + this.setState({currentSound: soundData.name || soundData.url}); + }); } _onSoundUploadChanged(e) { @@ -63,7 +60,7 @@ export default class NotificationsSettingsTab extends React.Component { }); } - async _saveSound (e) { + async _saveSound(e) { e.stopPropagation(); e.preventDefault(); if (!this.state.uploadedFile) { @@ -98,10 +95,10 @@ export default class NotificationsSettingsTab extends React.Component { }); } - _clearSound (e) { + _clearSound(e) { e.stopPropagation(); e.preventDefault(); - const room = client.getRoom(this.props.roomId); + const room = MatrixClientPeg.get().getRoom(this.props.roomId); Notifier.clearRoomSound(room); this.setState({ @@ -110,8 +107,6 @@ export default class NotificationsSettingsTab extends React.Component { } render() { - const client = MatrixClientPeg.get(); - return (
{_t("Notifications")}
From 776210c135f4904116c0f57177a9bd9514b11b6a Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 19 Apr 2019 22:31:51 +0100 Subject: [PATCH 06/19] Use settings store --- src/Notifier.js | 25 ++++++------------ .../tabs/room/NotificationSettingsTab.js | 26 ++++++++++--------- src/settings/Settings.js | 5 ++++ 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 43d599ae0d5..d5810fe30de 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -96,28 +96,19 @@ const Notifier = { } }, - setRoomSound: function(room, soundData) { - return MatrixClientPeg.get().setRoomAccountData(room.roomId, "uk.half-shot.notification.sound", soundData); - }, - - clearRoomSound: function(room) { - return room.setAccountData("uk.half-shot.notification.sound", null); - }, - - getSoundForRoom: async function(room) { + getSoundForRoom: async function(roomId) { // We do no caching here because the SDK caches the event content // and the browser will cache the sound. - let ev = await room.getAccountData("uk.half-shot.notification.sound"); - if (!ev || !ev.getContent()) { - // Check the account data. - ev = await MatrixClientPeg.get().getAccountData("uk.half-shot.notification.sound"); - if (!ev) { + let content = SettingsStore.getValue("notificationSound", roomId); + if (!content) { + content = SettingsStore.getValue("notificationSound"); + if (!content) { return null; } } - const content = ev.getContent(); + if (!content.url) { - console.warn(`${room.roomId} has custom notification sound event, but no url key`); + console.warn(`${roomId} has custom notification sound event, but no url key`); return null; } @@ -130,7 +121,7 @@ const Notifier = { }, _playAudioNotification: function(ev, room) { - this.getSoundForRoom(room).then((sound) => { + this.getSoundForRoom(room.roomId).then((sound) => { console.log(`Got sound ${sound.name || "default"} for ${room.roomId}`); // XXX: How do we ensure this is a sound file and not // going to be exploited? diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index a911ec3e4f9..062d54988a2 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -20,6 +20,7 @@ import {_t} from "../../../../../languageHandler"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; +import SettingsStore from '../../../../../settings/SettingsStore'; export default class NotificationsSettingsTab extends React.Component { static propTypes = { @@ -37,8 +38,7 @@ export default class NotificationsSettingsTab extends React.Component { } componentWillMount() { - const room = MatrixClientPeg.get().getRoom(this.props.roomId); - Notifier.getSoundForRoom(room).then((soundData) => { + Notifier.getSoundForRoom(this.props.roomId).then((soundData) => { if (!soundData) { return; } @@ -79,14 +79,17 @@ export default class NotificationsSettingsTab extends React.Component { }, ); - const room = MatrixClientPeg.get().getRoom(this.props.roomId); - - await Notifier.setRoomSound(room, { - name: this.state.uploadedFile.name, - type: type, - size: this.state.uploadedFile.size, - url, - }); + await SettingsStore.setValue( + "notificationSound", + this.props.roomId, + "room-account", + { + name: this.state.uploadedFile.name, + type: type, + size: this.state.uploadedFile.size, + url, + }, + ); this.setState({ uploadedFile: null, @@ -98,8 +101,7 @@ export default class NotificationsSettingsTab extends React.Component { _clearSound(e) { e.stopPropagation(); e.preventDefault(); - const room = MatrixClientPeg.get().getRoom(this.props.roomId); - Notifier.clearRoomSound(room); + SettingsStore.setValue("notificationSound", this.props.roomId, "room-account", null); this.setState({ currentSound: "default", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 35baa718b92..b40213e514b 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -26,6 +26,7 @@ import ThemeController from './controllers/ThemeController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; +const LEVELS_ROOM_OR_ACCOUNT = ['room-account', 'account']; const LEVELS_ROOM_SETTINGS_WITH_ROOM = ['device', 'room-device', 'room-account', 'account', 'config', 'room']; const LEVELS_ACCOUNT_SETTINGS = ['device', 'account', 'config']; const LEVELS_FEATURE = ['device', 'config']; @@ -315,6 +316,10 @@ export const SETTINGS = { default: false, controller: new NotificationsEnabledController(), }, + "notificationSound": { + supportedLevels: LEVELS_ROOM_OR_ACCOUNT, + default: false, + }, "notificationBodyEnabled": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: true, From b11050d32776c7995f9abd9953d7cebdcb899e7e Mon Sep 17 00:00:00 2001 From: Katie Wolfe Date: Sun, 21 Apr 2019 12:35:59 +0100 Subject: [PATCH 07/19] Check for sound before logging it Co-Authored-By: Half-Shot --- src/Notifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Notifier.js b/src/Notifier.js index d5810fe30de..c0c7fbdb946 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -122,7 +122,7 @@ const Notifier = { _playAudioNotification: function(ev, room) { this.getSoundForRoom(room.roomId).then((sound) => { - console.log(`Got sound ${sound.name || "default"} for ${room.roomId}`); + console.log(`Got sound ${sound && sound.name ? sound.name : "default"} for ${room.roomId}`); // XXX: How do we ensure this is a sound file and not // going to be exploited? const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); From 0f2cd6ea73c36015720293fcc20baf285a25d0be Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 21 Apr 2019 18:01:26 +0100 Subject: [PATCH 08/19] Stick behind a feature flag --- src/Notifier.js | 14 +++++++------- .../views/dialogs/RoomSettingsDialog.js | 15 ++++++++++----- .../settings/tabs/room/NotificationSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 +- src/settings/Settings.js | 6 ++++++ 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index d5810fe30de..2aad99875c8 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -120,11 +120,11 @@ const Notifier = { }; }, - _playAudioNotification: function(ev, room) { - this.getSoundForRoom(room.roomId).then((sound) => { - console.log(`Got sound ${sound.name || "default"} for ${room.roomId}`); - // XXX: How do we ensure this is a sound file and not - // going to be exploited? + _playAudioNotification: async function(ev, room) { + const sound = SettingsStore.isFeatureEnabled("feature_notification_sounds") ? await this.getSoundForRoom(room.roomId) : null; + console.log(`Got sound ${sound.name || "default"} for ${room.roomId}`); + // XXX: How do we ensure this is a sound file and not going to be exploited? + try { const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); let audioElement = selector; if (!selector) { @@ -139,9 +139,9 @@ const Notifier = { document.body.appendChild(audioElement); } audioElement.play(); - }).catch((ex) => { + } catch (ex) { console.warn("Caught error when trying to fetch room notification sound:", ex); - }); + } }, start: function() { diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index caed9580031..180148aead4 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -25,6 +25,7 @@ import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsT import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; +import SettingsStore from '../../../settings/SettingsStore'; export default class RoomSettingsDialog extends React.Component { static propTypes = { @@ -50,11 +51,15 @@ export default class RoomSettingsDialog extends React.Component { "mx_RoomSettingsDialog_rolesIcon", , )); - tabs.push(new Tab( - _td("Notifications"), - "mx_RoomSettingsDialog_rolesIcon", - , - )); + + if (SettingsStore.isFeatureEnabled("feature_notification_sounds")) { + tabs.push(new Tab( + _td("Notifications"), + "mx_RoomSettingsDialog_rolesIcon", + , + )); + } + tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 062d54988a2..6199804cded 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -115,7 +115,7 @@ export default class NotificationsSettingsTab extends React.Component {
{_t("Sounds")}
- {_t("Notification sound")}: {this.state.currentSound} + {_t("Custom Notification Sounds")}: {this.state.currentSound}

{_t("Set a new custom sound")}

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7f94bdc9cda..feeff019c02 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1616,7 +1616,7 @@ "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", "Sounds": "Sounds", - "Notification sound": "Notification sound", + "Custom Notification Sounds": "Notification sound", "Set a new custom sound": "Set a new custom sound", "Reset to default sound": "Reset to default sound" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index b40213e514b..b0014ae4b66 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -119,6 +119,12 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_notification_sounds": { + isFeature: true, + displayName: _td("Custom Notification Sounds"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 8258e933375a4c02023c7ed599002eba83bd2b99 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 21 Apr 2019 18:03:54 +0100 Subject: [PATCH 09/19] Use m.notification.sound --- src/settings/handlers/AccountSettingsHandler.js | 4 ++++ src/settings/handlers/RoomSettingsHandler.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index 71cef52c4eb..979966ac79b 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -73,6 +73,10 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return !content['disable']; } + if (settingName === "notificationsEnabled") { + return this._getSettings("m.notification.sound"); + } + // Special case for breadcrumbs if (settingName === "breadcrumb_rooms") { const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {}; diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.js index 79626e21860..e929e81c885 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.js @@ -67,6 +67,10 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl if (typeof(content['disable']) !== "boolean") return null; return !content['disable']; } + + if (settingName === "notificationsEnabled") { + return this._getSettings(roomId, "m.notification.sound"); + } const settings = this._getSettings(roomId) || {}; return settings[settingName]; From 9de8920869fc502621f9b5f723ffd3d517d52590 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 22 Apr 2019 23:04:03 +0100 Subject: [PATCH 10/19] Remove trailing space --- src/settings/handlers/RoomSettingsHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.js index e929e81c885..894190b23e2 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.js @@ -67,7 +67,7 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl if (typeof(content['disable']) !== "boolean") return null; return !content['disable']; } - + if (settingName === "notificationsEnabled") { return this._getSettings(roomId, "m.notification.sound"); } From 020d210cb00b1274c4f3d863b4b46661bbbf98b7 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 23 Apr 2019 16:23:58 +0100 Subject: [PATCH 11/19] Use uk.half-shot.* --- src/settings/handlers/AccountSettingsHandler.js | 4 ++-- src/settings/handlers/RoomSettingsHandler.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index 979966ac79b..e34f5b6aae6 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -73,8 +73,8 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return !content['disable']; } - if (settingName === "notificationsEnabled") { - return this._getSettings("m.notification.sound"); + if (settingName === "notificationSound") { + return this._getSettings("uk.half-shot.notification.sound"); } // Special case for breadcrumbs diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.js index 894190b23e2..470641f9ddb 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.js @@ -68,8 +68,8 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl return !content['disable']; } - if (settingName === "notificationsEnabled") { - return this._getSettings(roomId, "m.notification.sound"); + if (settingName === "notificationSound") { + return this._getSettings(roomId, "uk.half-shot.notification.sound"); } const settings = this._getSettings(roomId) || {}; From 64a384477e567fb7d737c5389edca582e47d8d47 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 12 May 2019 17:14:21 +0100 Subject: [PATCH 12/19] Resolve issues --- src/Notifier.js | 16 +++++---- .../tabs/room/NotificationSettingsTab.js | 34 ++++++++++++------- src/i18n/strings/en_EN.json | 10 +++--- src/settings/Settings.js | 1 + 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index ca0a24d5939..8c62a9e8226 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -97,20 +97,24 @@ const Notifier = { }, getSoundForRoom: async function(roomId) { - // We do no caching here because the SDK caches the event content + // We do no caching here because the SDK caches setting // and the browser will cache the sound. let content = SettingsStore.getValue("notificationSound", roomId); if (!content) { - content = SettingsStore.getValue("notificationSound"); - if (!content) { - return null; - } + return null; } if (!content.url) { console.warn(`${roomId} has custom notification sound event, but no url key`); return null; } + + if (!content.url.startsWith("mxc://")) { + console.warn(`${roomId} has custom notification sound event, but url is not a mxc url`); + return null; + } + + // Ideally in here we could use MSC1310 to detect the type of file, and reject it. return { url: MatrixClientPeg.get().mxcUrlToHttp(content.url), @@ -123,7 +127,7 @@ const Notifier = { _playAudioNotification: async function(ev, room) { const sound = SettingsStore.isFeatureEnabled("feature_notification_sounds") ? await this.getSoundForRoom(room.roomId) : null; console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`); - // XXX: How do we ensure this is a sound file and not going to be exploited? + try { const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); let audioElement = selector; diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 6199804cded..e7ed14b4915 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -20,12 +20,12 @@ import {_t} from "../../../../../languageHandler"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; -import SettingsStore from '../../../../../settings/SettingsStore'; +import SettingsStore from '../../../../../settings/SettingsStore'; +import { SettingLevel } from '../../../../../settings/SettingsStore'; export default class NotificationsSettingsTab extends React.Component { static propTypes = { roomId: PropTypes.string.isRequired, - closeSettingsFn: PropTypes.func.isRequired, }; constructor() { @@ -46,7 +46,7 @@ export default class NotificationsSettingsTab extends React.Component { }); } - _onSoundUploadChanged(e) { + async _onSoundUploadChanged(e) { if (!e.target.files || !e.target.files.length) { this.setState({ uploadedFile: null, @@ -58,11 +58,18 @@ export default class NotificationsSettingsTab extends React.Component { this.setState({ uploadedFile: file, }); + + try { + await this._saveSound(); + } catch (ex) { + console.error( + `Unable to save notification sound for ${this.props.roomId}`, + ex, + ); + } } - async _saveSound(e) { - e.stopPropagation(); - e.preventDefault(); + async _saveSound() { if (!this.state.uploadedFile) { return; } @@ -82,7 +89,8 @@ export default class NotificationsSettingsTab extends React.Component { await SettingsStore.setValue( "notificationSound", this.props.roomId, - "room-account", + SettingsStore. + SettingLevel.ROOM_ACCOUNT, { name: this.state.uploadedFile.name, type: type, @@ -101,7 +109,12 @@ export default class NotificationsSettingsTab extends React.Component { _clearSound(e) { e.stopPropagation(); e.preventDefault(); - SettingsStore.setValue("notificationSound", this.props.roomId, "room-account", null); + SettingsStore.setValue( + "notificationSound", + this.props.roomId, + SettingLevel.ROOM_ACCOUNT, + null, + ); this.setState({ currentSound: "default", @@ -119,11 +132,8 @@ export default class NotificationsSettingsTab extends React.Component {

{_t("Set a new custom sound")}

-
+ - - {_t("Save")} -
{_t("Reset to default sound")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d396c70e4c7..d841d5a34b9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -297,6 +297,7 @@ "Show recent room avatars above the room list": "Show recent room avatars above the room list", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", + "Custom Notification Sounds": "Custom Notification Sounds", "React to messages with emoji": "React to messages with emoji", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", @@ -613,6 +614,9 @@ "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", + "Sounds": "Sounds", + "Set a new custom sound": "Set a new custom sound", + "Reset to default sound": "Reset to default sound", "Change room avatar": "Change room avatar", "Change room name": "Change room name", "Change main address for the room": "Change main address for the room", @@ -1619,9 +1623,5 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", - "Sounds": "Sounds", - "Custom Notification Sounds": "Notification sound", - "Set a new custom sound": "Set a new custom sound", - "Reset to default sound": "Reset to default sound" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 3ef2e8dd866..e5ae504b53a 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -122,6 +122,7 @@ export const SETTINGS = { "feature_notification_sounds": { isFeature: true, displayName: _td("Custom Notification Sounds"), + }, "feature_reactions": { isFeature: true, displayName: _td("React to messages with emoji"), From 626cb46915d3ff535156dd1114114fadb4942d2d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 14 May 2019 21:05:22 +0100 Subject: [PATCH 13/19] Cleanup interface buttons --- res/css/views/settings/_Notifications.scss | 8 ++++++++ .../tabs/room/NotificationSettingsTab.js | 18 +++++++++++++++--- src/i18n/strings/en_EN.json | 3 ++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index d6c0b5dbeb4..b21e7a51137 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -71,3 +71,11 @@ limitations under the License. .mx_UserNotifSettings_notifTable .mx_Spinner { position: absolute; } + +.mx_NotificationSound_soundUpload { + display: none; +} + +.mx_NotificationSound_resetSound { + margin-left: 5px; +} \ No newline at end of file diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index e7ed14b4915..9d0848baf43 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -46,6 +46,13 @@ export default class NotificationsSettingsTab extends React.Component { }); } + async _triggerUploader(e) { + e.stopPropagation(); + e.preventDefault(); + + this.refs.soundUpload.click(); + } + async _onSoundUploadChanged(e) { if (!e.target.files || !e.target.files.length) { this.setState({ @@ -133,10 +140,15 @@ export default class NotificationsSettingsTab extends React.Component {

{_t("Set a new custom sound")}

- +
- - {_t("Reset to default sound")} + + + {_t("Browse")} + + + + {_t("Reset")}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d841d5a34b9..5070bb7d303 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -616,7 +616,8 @@ "URL Previews": "URL Previews", "Sounds": "Sounds", "Set a new custom sound": "Set a new custom sound", - "Reset to default sound": "Reset to default sound", + "Browse": "Browse", + "Reset": "Reset", "Change room avatar": "Change room avatar", "Change room name": "Change room name", "Change main address for the room": "Change main address for the room", From 079bdd44a4cfc5d45df54932db08a9bdf0f4e5c9 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 14 May 2019 21:07:29 +0100 Subject: [PATCH 14/19] Update il8n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6747e678333..70128c4f8b6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -301,7 +301,6 @@ "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", "Custom Notification Sounds": "Custom Notification Sounds", - "React to messages with emoji": "React to messages with emoji", "React to messages with emoji (refresh to apply changes)": "React to messages with emoji (refresh to apply changes)", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", From d752de09728042a0d3dd7c1c42cf5c59c91259fa Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 15 May 2019 15:52:42 +0100 Subject: [PATCH 15/19] Improve UX --- res/css/views/settings/_Notifications.scss | 17 ++++++++- .../tabs/room/NotificationSettingsTab.js | 36 ++++++++++++++----- src/i18n/strings/en_EN.json | 4 ++- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index b21e7a51137..773ea055df4 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -76,6 +76,21 @@ limitations under the License. display: none; } -.mx_NotificationSound_resetSound { +.mx_NotificationSound_browse { + color: $accent-color; + border: 1px solid $accent-color; + background-color: transparent; +} + +.mx_NotificationSound_save { margin-left: 5px; + color: white; + background-color: $accent-color; +} + +.mx_NotificationSound_resetSound { + margin-top: 5px; + color: white; + border: $warning-color; + background-color: $warning-color; } \ No newline at end of file diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 9d0848baf43..6df5b2e4694 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -20,7 +20,7 @@ import {_t} from "../../../../../languageHandler"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; -import SettingsStore from '../../../../../settings/SettingsStore'; +import SettingsStore from '../../../../../settings/SettingsStore'; import { SettingLevel } from '../../../../../settings/SettingsStore'; export default class NotificationsSettingsTab extends React.Component { @@ -65,14 +65,19 @@ export default class NotificationsSettingsTab extends React.Component { this.setState({ uploadedFile: file, }); + } + + async _onClickSaveSound(e) { + e.stopPropagation(); + e.preventDefault(); try { await this._saveSound(); } catch (ex) { console.error( `Unable to save notification sound for ${this.props.roomId}`, - ex, ); + console.error(ex); } } @@ -80,6 +85,7 @@ export default class NotificationsSettingsTab extends React.Component { if (!this.state.uploadedFile) { return; } + let type = this.state.uploadedFile.type; if (type === "video/ogg") { // XXX: I've observed browsers allowing users to pick a audio/ogg files, @@ -87,6 +93,7 @@ export default class NotificationsSettingsTab extends React.Component { // suck at detecting mimetypes. type = "audio/ogg"; } + const url = await MatrixClientPeg.get().uploadContent( this.state.uploadedFile, { type, @@ -96,7 +103,6 @@ export default class NotificationsSettingsTab extends React.Component { await SettingsStore.setValue( "notificationSound", this.props.roomId, - SettingsStore. SettingLevel.ROOM_ACCOUNT, { name: this.state.uploadedFile.name, @@ -108,7 +114,6 @@ export default class NotificationsSettingsTab extends React.Component { this.setState({ uploadedFile: null, - uploadedFileUrl: null, currentSound: this.state.uploadedFile.name, }); } @@ -129,13 +134,25 @@ export default class NotificationsSettingsTab extends React.Component { } render() { + let currentUploadedFile = null; + if (this.state.uploadedFile) { + currentUploadedFile = ( +
+ {_t("Uploaded sound")}: {this.state.uploadedFile.name} +
+ ); + } + return (
{_t("Notifications")}
{_t("Sounds")}
- {_t("Custom Notification Sounds")}: {this.state.currentSound} + {_t("Notification sound")}: {this.state.currentSound}
+ + {_t("Reset")} +

{_t("Set a new custom sound")}

@@ -143,13 +160,16 @@ export default class NotificationsSettingsTab extends React.Component { - + {currentUploadedFile} + + {_t("Browse")} - - {_t("Reset")} + + {_t("Save")} +
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 70128c4f8b6..91829a80b4e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -617,10 +617,12 @@ "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", + "Uploaded sound": "Uploaded sound", "Sounds": "Sounds", + "Notification sound": "Notification sound", + "Reset": "Reset", "Set a new custom sound": "Set a new custom sound", "Browse": "Browse", - "Reset": "Reset", "Change room avatar": "Change room avatar", "Change room name": "Change room name", "Change main address for the room": "Change main address for the room", From 96c20ea53649142ef30488df2b39488fba4c2788 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 31 May 2019 11:40:23 +0100 Subject: [PATCH 16/19] Add missing keys to notif sound setting --- src/settings/Settings.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 1b2096b5d31..6011c5cd433 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -129,6 +129,8 @@ export const SETTINGS = { "feature_notification_sounds": { isFeature: true, displayName: _td("Custom Notification Sounds"), + supportedLevels: LEVELS_FEATURE, + default: false, }, "feature_reactions": { isFeature: true, From 300095f50fa532a6fea212351a5406cf4ddf4aca Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 3 Jun 2019 17:35:15 +0100 Subject: [PATCH 17/19] Remove labs flag for custom notif sounds --- src/Notifier.js | 4 ++-- src/components/views/dialogs/RoomSettingsDialog.js | 12 +++++------- .../settings/tabs/room/NotificationSettingsTab.js | 4 ++-- src/settings/Settings.js | 6 ------ src/settings/handlers/AccountSettingsHandler.js | 4 ---- src/settings/handlers/RoomSettingsHandler.js | 4 ---- 6 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/Notifier.js b/src/Notifier.js index 7410fe316a3..ef4102ee917 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -103,7 +103,7 @@ const Notifier = { getSoundForRoom: async function(roomId) { // We do no caching here because the SDK caches setting // and the browser will cache the sound. - let content = SettingsStore.getValue("notificationSound", roomId); + const content = SettingsStore.getValue("notificationSound", roomId); if (!content) { return null; } @@ -129,7 +129,7 @@ const Notifier = { }, _playAudioNotification: async function(ev, room) { - const sound = SettingsStore.isFeatureEnabled("feature_notification_sounds") ? await this.getSoundForRoom(room.roomId) : null; + const sound = await this.getSoundForRoom(room.roomId); console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`); try { diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 72ebf316e11..e657a5dbf54 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -70,13 +70,11 @@ export default class RoomSettingsDialog extends React.Component { , )); - if (SettingsStore.isFeatureEnabled("feature_notification_sounds")) { - tabs.push(new Tab( - _td("Notifications"), - "mx_RoomSettingsDialog_rolesIcon", - , - )); - } + tabs.push(new Tab( + _td("Notifications"), + "mx_RoomSettingsDialog_rolesIcon", + , + )); tabs.push(new Tab( _td("Advanced"), diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 6df5b2e4694..28c6e7dd5ec 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -85,7 +85,7 @@ export default class NotificationsSettingsTab extends React.Component { if (!this.state.uploadedFile) { return; } - + let type = this.state.uploadedFile.type; if (type === "video/ogg") { // XXX: I've observed browsers allowing users to pick a audio/ogg files, @@ -149,7 +149,7 @@ export default class NotificationsSettingsTab extends React.Component {
{_t("Sounds")}
- {_t("Notification sound")}: {this.state.currentSound}
+ {_t("Notification sound")}: {this.state.currentSound}
{_t("Reset")} diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 6011c5cd433..b284c52d0f1 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -126,12 +126,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "feature_notification_sounds": { - isFeature: true, - displayName: _td("Custom Notification Sounds"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "feature_reactions": { isFeature: true, displayName: _td("React to messages with emoji (refresh to apply changes)"), diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index e34f5b6aae6..71cef52c4eb 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -73,10 +73,6 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return !content['disable']; } - if (settingName === "notificationSound") { - return this._getSettings("uk.half-shot.notification.sound"); - } - // Special case for breadcrumbs if (settingName === "breadcrumb_rooms") { const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {}; diff --git a/src/settings/handlers/RoomSettingsHandler.js b/src/settings/handlers/RoomSettingsHandler.js index 470641f9ddb..79626e21860 100644 --- a/src/settings/handlers/RoomSettingsHandler.js +++ b/src/settings/handlers/RoomSettingsHandler.js @@ -68,10 +68,6 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl return !content['disable']; } - if (settingName === "notificationSound") { - return this._getSettings(roomId, "uk.half-shot.notification.sound"); - } - const settings = this._getSettings(roomId) || {}; return settings[settingName]; } From 673a8fcb9df3118e4a4e9b50cb91d3787a86c19e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 3 Jun 2019 21:19:17 +0100 Subject: [PATCH 18/19] Update src/Notifier.js Co-Authored-By: Travis Ralston --- src/Notifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Notifier.js b/src/Notifier.js index ef4102ee917..0b0a5f69906 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -137,7 +137,7 @@ const Notifier = { let audioElement = selector; if (!selector) { if (!sound) { - console.error("Tried to play alert sound but missing #messageAudio"); + console.error("No audio element or sound to play for notification"); return; } audioElement = new Audio(sound.url); From 721607b696d7ce42457ecbc4d93189630af7243a Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 3 Jun 2019 21:19:58 +0100 Subject: [PATCH 19/19] Remove whitespace --- src/components/views/dialogs/RoomSettingsDialog.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index e657a5dbf54..15b04ea5040 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -69,13 +69,11 @@ export default class RoomSettingsDialog extends React.Component { "mx_RoomSettingsDialog_rolesIcon", , )); - tabs.push(new Tab( _td("Notifications"), "mx_RoomSettingsDialog_rolesIcon", , )); - tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon",