Merge pull request #5873 from matrix-org/travis/dnd

Labs: Add quick/cheap "do not disturb" flag
This commit is contained in:
Travis Ralston 2021-04-15 08:38:17 -06:00 committed by GitHub
commit bf70666f9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 0 deletions

View file

@ -117,6 +117,32 @@ limitations under the License.
.mx_UserMenu_headerButtons { .mx_UserMenu_headerButtons {
// No special styles: the rest of the layout happens to make it work. // No special styles: the rest of the layout happens to make it work.
} }
.mx_UserMenu_dnd {
width: 24px;
height: 24px;
margin-right: 8px;
position: relative;
&::before {
content: '';
position: absolute;
width: 24px;
height: 24px;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: $muted-fg-color;
}
&.mx_UserMenu_dnd_noisy::before {
mask-image: url('$(res)/img/element-icons/notifications.svg');
}
&.mx_UserMenu_dnd_muted::before {
mask-image: url('$(res)/img/element-icons/roomlist/notifications-off.svg');
}
}
} }
&.mx_UserMenu_minimized { &.mx_UserMenu_minimized {

View file

@ -383,6 +383,10 @@ export const Notifier = {
// don't bother notifying as user was recently active in this room // don't bother notifying as user was recently active in this room
return; return;
} }
if (SettingsStore.getValue("doNotDisturb")) {
// Don't bother the user if they didn't ask to be bothered
return;
}
if (this.isEnabled()) { if (this.isEnabled()) {
this._displayPopupNotification(ev, room); this._displayPopupNotification(ev, room);

View file

@ -74,6 +74,7 @@ interface IState {
export default class UserMenu extends React.Component<IProps, IState> { export default class UserMenu extends React.Component<IProps, IState> {
private dispatcherRef: string; private dispatcherRef: string;
private themeWatcherRef: string; private themeWatcherRef: string;
private dndWatcherRef: string;
private buttonRef: React.RefObject<HTMLButtonElement> = createRef(); private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
private tagStoreRef: fbEmitter.EventSubscription; private tagStoreRef: fbEmitter.EventSubscription;
@ -89,6 +90,9 @@ export default class UserMenu extends React.Component<IProps, IState> {
if (SettingsStore.getValue("feature_spaces")) { if (SettingsStore.getValue("feature_spaces")) {
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate); SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
} }
// Force update is the easiest way to trigger the UI update (we don't store state for this)
this.dndWatcherRef = SettingsStore.watchSetting("doNotDisturb", null, () => this.forceUpdate());
} }
private get hasHomePage(): boolean { private get hasHomePage(): boolean {
@ -103,6 +107,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
public componentWillUnmount() { public componentWillUnmount() {
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef); if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate);
this.tagStoreRef.remove(); this.tagStoreRef.remove();
@ -288,6 +293,12 @@ export default class UserMenu extends React.Component<IProps, IState> {
this.setState({contextMenuPosition: null}); // also close the menu this.setState({contextMenuPosition: null}); // also close the menu
}; };
private onDndToggle = (ev) => {
ev.stopPropagation();
const current = SettingsStore.getValue("doNotDisturb");
SettingsStore.setValue("doNotDisturb", null, SettingLevel.DEVICE, !current);
};
private renderContextMenu = (): React.ReactNode => { private renderContextMenu = (): React.ReactNode => {
if (!this.state.contextMenuPosition) return null; if (!this.state.contextMenuPosition) return null;
@ -534,6 +545,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
{/* masked image in CSS */} {/* masked image in CSS */}
</span> </span>
); );
let dnd;
if (this.state.selectedSpace) { if (this.state.selectedSpace) {
name = ( name = (
<div className="mx_UserMenu_doubleName"> <div className="mx_UserMenu_doubleName">
@ -560,6 +572,16 @@ export default class UserMenu extends React.Component<IProps, IState> {
</div> </div>
); );
isPrototype = true; isPrototype = true;
} else if (SettingsStore.getValue("feature_dnd")) {
const isDnd = SettingsStore.getValue("doNotDisturb");
dnd = <AccessibleButton
onClick={this.onDndToggle}
className={classNames({
"mx_UserMenu_dnd": true,
"mx_UserMenu_dnd_noisy": !isDnd,
"mx_UserMenu_dnd_muted": isDnd,
})}
/>;
} }
if (this.props.isMinimized) { if (this.props.isMinimized) {
name = null; name = null;
@ -595,6 +617,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
/> />
</span> </span>
{name} {name}
{dnd}
{buttons} {buttons}
</div> </div>
</ContextMenuButton> </ContextMenuButton>

View file

@ -786,6 +786,7 @@
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s", "%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
"Change notification settings": "Change notification settings", "Change notification settings": "Change notification settings",
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.", "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.",
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
"Send and receive voice messages (in development)": "Send and receive voice messages (in development)", "Send and receive voice messages (in development)": "Send and receive voice messages (in development)",
"Render LaTeX maths in messages": "Render LaTeX maths in messages", "Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.", "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.",

View file

@ -128,6 +128,12 @@ export const SETTINGS: {[setting: string]: ISetting} = {
default: false, default: false,
controller: new ReloadOnChangeController(), controller: new ReloadOnChangeController(),
}, },
"feature_dnd": {
isFeature: true,
displayName: _td("Show options to enable 'Do not disturb' mode"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_voice_messages": { "feature_voice_messages": {
isFeature: true, isFeature: true,
displayName: _td("Send and receive voice messages (in development)"), displayName: _td("Send and receive voice messages (in development)"),
@ -226,6 +232,10 @@ export const SETTINGS: {[setting: string]: ISetting} = {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false, default: false,
}, },
"doNotDisturb": {
supportedLevels: [SettingLevel.DEVICE],
default: false,
},
"mjolnirRooms": { "mjolnirRooms": {
supportedLevels: [SettingLevel.ACCOUNT], supportedLevels: [SettingLevel.ACCOUNT],
default: [], default: [],