Notification sounds (#367)
* Basic notification sound support * Add settings option for notification sounds * Allow sound without desktop notifications
This commit is contained in:
parent
a2655ee6a5
commit
dc6e153b92
7 changed files with 78 additions and 24 deletions
BIN
public/sound/notification.ogg
Executable file
BIN
public/sound/notification.ogg
Executable file
Binary file not shown.
|
@ -7,7 +7,7 @@ import cons from '../../../client/state/cons';
|
||||||
import settings from '../../../client/state/settings';
|
import settings from '../../../client/state/settings';
|
||||||
import {
|
import {
|
||||||
toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents,
|
toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents,
|
||||||
toggleNotifications,
|
toggleNotifications, toggleNotificationSounds,
|
||||||
} from '../../../client/action/settings';
|
} from '../../../client/action/settings';
|
||||||
import logout from '../../../client/action/logout';
|
import logout from '../../../client/action/logout';
|
||||||
import { usePermission } from '../../hooks/usePermission';
|
import { usePermission } from '../../hooks/usePermission';
|
||||||
|
@ -158,6 +158,16 @@ function NotificationsSection() {
|
||||||
options={renderOptions()}
|
options={renderOptions()}
|
||||||
content={<Text variant="b3">Show notifications when new messages arrive.</Text>}
|
content={<Text variant="b3">Show notifications when new messages arrive.</Text>}
|
||||||
/>
|
/>
|
||||||
|
<SettingTile
|
||||||
|
title="Play notification sounds"
|
||||||
|
options={(
|
||||||
|
<Toggle
|
||||||
|
isActive={settings.isNotificationSounds}
|
||||||
|
onToggle={() => { toggleNotificationSounds(); updateState({}); }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
content={<Text variant="b3">Play a sound when new messages arrive.</Text>}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -200,7 +210,7 @@ function AboutSection() {
|
||||||
<div className="set-about__branding">
|
<div className="set-about__branding">
|
||||||
<img width="60" height="60" src={CinnySVG} alt="Cinny logo" />
|
<img width="60" height="60" src={CinnySVG} alt="Cinny logo" />
|
||||||
<div>
|
<div>
|
||||||
<Text variant="h2" weight='medium'>
|
<Text variant="h2" weight="medium">
|
||||||
Cinny
|
Cinny
|
||||||
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span>
|
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -223,6 +233,10 @@ function AboutSection() {
|
||||||
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
|
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
|
||||||
<Text>The <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twemoji</a> emoji art is © <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twitter, Inc and other contributors</a> used under the terms of <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noreferrer noopener">CC-BY 4.0</a>.</Text>
|
<Text>The <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twemoji</a> emoji art is © <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twitter, Inc and other contributors</a> used under the terms of <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noreferrer noopener">CC-BY 4.0</a>.</Text>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
|
||||||
|
<Text>The <a href="https://material.io/design/sound/sound-resources.html" target="_blank" rel="noreferrer noopener">Material sound resources</a> are © <a href="https://google.com" target="_blank" rel="noreferrer noopener">Google</a> used under the terms of <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noreferrer noopener">CC-BY 4.0</a>.</Text>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -36,3 +36,9 @@ export function toggleNotifications() {
|
||||||
type: cons.actions.settings.TOGGLE_NOTIFICATIONS,
|
type: cons.actions.settings.TOGGLE_NOTIFICATIONS,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleNotificationSounds() {
|
||||||
|
appDispatcher.dispatch({
|
||||||
|
type: cons.actions.settings.TOGGLE_NOTIFICATION_SOUNDS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import cons from './cons';
|
||||||
import navigation from './navigation';
|
import navigation from './navigation';
|
||||||
import settings from './settings';
|
import settings from './settings';
|
||||||
|
|
||||||
|
import NotificationSound from '../../../public/sound/notification.ogg';
|
||||||
|
|
||||||
function isNotifEvent(mEvent) {
|
function isNotifEvent(mEvent) {
|
||||||
const eType = mEvent.getType();
|
const eType = mEvent.getType();
|
||||||
if (!cons.supportEventTypes.includes(eType)) return false;
|
if (!cons.supportEventTypes.includes(eType)) return false;
|
||||||
|
@ -185,7 +187,7 @@ class Notifications extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _displayPopupNoti(mEvent, room) {
|
async _displayPopupNoti(mEvent, room) {
|
||||||
if (!settings.showNotifications) return;
|
if (!settings.showNotifications && !settings.isNotificationSounds) return;
|
||||||
|
|
||||||
const actions = this.matrixClient.getPushActionsForEvent(mEvent);
|
const actions = this.matrixClient.getPushActionsForEvent(mEvent);
|
||||||
if (!actions?.notify) return;
|
if (!actions?.notify) return;
|
||||||
|
@ -196,28 +198,43 @@ class Notifications extends EventEmitter {
|
||||||
await mEvent.attemptDecryption(this.matrixClient.crypto);
|
await mEvent.attemptDecryption(this.matrixClient.crypto);
|
||||||
}
|
}
|
||||||
|
|
||||||
let title;
|
if (settings.showNotifications) {
|
||||||
if (!mEvent.sender || room.name === mEvent.sender.name) {
|
let title;
|
||||||
title = room.name;
|
if (!mEvent.sender || room.name === mEvent.sender.name) {
|
||||||
} else if (mEvent.sender) {
|
title = room.name;
|
||||||
title = `${mEvent.sender.name} (${room.name})`;
|
} else if (mEvent.sender) {
|
||||||
|
title = `${mEvent.sender.name} (${room.name})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconSize = 36;
|
||||||
|
const icon = await renderAvatar({
|
||||||
|
text: mEvent.sender.name,
|
||||||
|
bgColor: cssColorMXID(mEvent.getSender()),
|
||||||
|
imageSrc: mEvent.sender?.getAvatarUrl(this.matrixClient.baseUrl, iconSize, iconSize, 'crop'),
|
||||||
|
size: iconSize,
|
||||||
|
borderRadius: 8,
|
||||||
|
scale: 8,
|
||||||
|
});
|
||||||
|
|
||||||
|
const noti = new window.Notification(title, {
|
||||||
|
body: mEvent.getContent().body,
|
||||||
|
icon,
|
||||||
|
silent: settings.isNotificationSounds,
|
||||||
|
});
|
||||||
|
if (settings.isNotificationSounds) {
|
||||||
|
noti.onshow = () => this._playNotiSounds();
|
||||||
|
}
|
||||||
|
noti.onclick = () => selectRoom(room.roomId, mEvent.getId());
|
||||||
|
} else {
|
||||||
|
this._playNotiSounds();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const iconSize = 36;
|
_playNotiSounds() {
|
||||||
const icon = await renderAvatar({
|
if (!this._notiAudio) {
|
||||||
text: mEvent.sender.name,
|
this._notiAudio = new Audio(NotificationSound);
|
||||||
bgColor: cssColorMXID(mEvent.getSender()),
|
}
|
||||||
imageSrc: mEvent.sender?.getAvatarUrl(this.matrixClient.baseUrl, iconSize, iconSize, 'crop'),
|
this._notiAudio.play();
|
||||||
size: iconSize,
|
|
||||||
borderRadius: 8,
|
|
||||||
scale: 8,
|
|
||||||
});
|
|
||||||
|
|
||||||
const noti = new window.Notification(title, {
|
|
||||||
body: mEvent.getContent().body,
|
|
||||||
icon,
|
|
||||||
});
|
|
||||||
noti.onclick = () => selectRoom(room.roomId, mEvent.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_listenEvents() {
|
_listenEvents() {
|
||||||
|
|
|
@ -67,6 +67,7 @@ const cons = {
|
||||||
TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
|
TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
|
||||||
TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
|
TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
|
||||||
TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS',
|
TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS',
|
||||||
|
TOGGLE_NOTIFICATION_SOUNDS: 'TOGGLE_NOTIFICATION_SOUNDS',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
|
@ -135,6 +136,7 @@ const cons = {
|
||||||
MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
|
MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
|
||||||
NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
|
NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
|
||||||
NOTIFICATIONS_TOGGLED: 'NOTIFICATIONS_TOGGLED',
|
NOTIFICATIONS_TOGGLED: 'NOTIFICATIONS_TOGGLED',
|
||||||
|
NOTIFICATION_SOUNDS_TOGGLED: 'NOTIFICATION_SOUNDS_TOGGLED',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Settings extends EventEmitter {
|
||||||
this.hideMembershipEvents = this.getHideMembershipEvents();
|
this.hideMembershipEvents = this.getHideMembershipEvents();
|
||||||
this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
|
this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
|
||||||
this._showNotifications = this.getShowNotifications();
|
this._showNotifications = this.getShowNotifications();
|
||||||
|
this.isNotificationSounds = this.getIsNotificationSounds();
|
||||||
|
|
||||||
this.isTouchScreenDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);
|
this.isTouchScreenDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);
|
||||||
}
|
}
|
||||||
|
@ -125,6 +126,15 @@ class Settings extends EventEmitter {
|
||||||
return settings.showNotifications;
|
return settings.showNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsNotificationSounds() {
|
||||||
|
if (typeof this.isNotificationSounds === 'boolean') return this.isNotificationSounds;
|
||||||
|
|
||||||
|
const settings = getSettings();
|
||||||
|
if (settings === null) return true;
|
||||||
|
if (typeof settings.isNotificationSounds === 'undefined') return true;
|
||||||
|
return settings.isNotificationSounds;
|
||||||
|
}
|
||||||
|
|
||||||
setter(action) {
|
setter(action) {
|
||||||
const actions = {
|
const actions = {
|
||||||
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
||||||
|
@ -164,6 +174,11 @@ class Settings extends EventEmitter {
|
||||||
setSettings('showNotifications', this._showNotifications);
|
setSettings('showNotifications', this._showNotifications);
|
||||||
this.emit(cons.events.settings.NOTIFICATIONS_TOGGLED, this._showNotifications);
|
this.emit(cons.events.settings.NOTIFICATIONS_TOGGLED, this._showNotifications);
|
||||||
},
|
},
|
||||||
|
[cons.actions.settings.TOGGLE_NOTIFICATION_SOUNDS]: () => {
|
||||||
|
this.isNotificationSounds = !this.isNotificationSounds;
|
||||||
|
setSettings('isNotificationSounds', this.isNotificationSounds);
|
||||||
|
this.emit(cons.events.settings.NOTIFICATION_SOUNDS_TOGGLED, this.isNotificationSounds);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
actions[action.type]?.();
|
actions[action.type]?.();
|
||||||
|
|
|
@ -39,7 +39,7 @@ module.exports = {
|
||||||
use: ['html-loader'],
|
use: ['html-loader'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpe?g|gif|otf|ttf|woff|woff2)$/,
|
test: /\.(png|jpe?g|gif|otf|ttf|woff|woff2|ogg)$/,
|
||||||
type: 'asset/resource',
|
type: 'asset/resource',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue