Browse Source

Mute options

Set local timeout by nearest mute_until
master
Eduard Kuzmenko 3 years ago
parent
commit
b15c51d5d1
  1. 15
      src/components/chat/topbar.ts
  2. 5
      src/components/dialogsContextMenu.ts
  3. 2
      src/components/peerProfile.ts
  4. 77
      src/components/popups/mute.ts
  5. 2
      src/components/sidebarRight/tabs/editContact.ts
  6. 6
      src/lang.ts
  7. 16
      src/lib/appManagers/appMessagesManager.ts
  8. 50
      src/lib/appManagers/appNotificationsManager.ts
  9. 24
      src/scss/partials/popups/_mute.scss
  10. 1
      src/scss/style.scss

15
src/components/chat/topbar.ts

@ -49,6 +49,7 @@ import { NULL_PEER_ID } from "../../lib/mtproto/mtproto_config";
import IS_GROUP_CALL_SUPPORTED from "../../environment/groupCallSupport"; import IS_GROUP_CALL_SUPPORTED from "../../environment/groupCallSupport";
import IS_CALL_SUPPORTED from "../../environment/callSupport"; import IS_CALL_SUPPORTED from "../../environment/callSupport";
import { CallType } from "../../lib/calls/types"; import { CallType } from "../../lib/calls/types";
import PopupMute from "../popups/mute";
type ButtonToVerify = {element?: HTMLElement, verify: () => boolean}; type ButtonToVerify = {element?: HTMLElement, verify: () => boolean};
@ -319,15 +320,13 @@ export default class ChatTopbar {
}, */{ }, */{
icon: 'mute', icon: 'mute',
text: 'ChatList.Context.Mute', text: 'ChatList.Context.Mute',
onClick: () => { onClick: this.onMuteClick,
this.appMessagesManager.mutePeer(this.peerId);
},
verify: () => this.chat.type === 'chat' && rootScope.myId !== this.peerId && !this.appNotificationsManager.isPeerLocalMuted(this.peerId, false) verify: () => this.chat.type === 'chat' && rootScope.myId !== this.peerId && !this.appNotificationsManager.isPeerLocalMuted(this.peerId, false)
}, { }, {
icon: 'unmute', icon: 'unmute',
text: 'ChatList.Context.Unmute', text: 'ChatList.Context.Unmute',
onClick: () => { onClick: () => {
this.appMessagesManager.mutePeer(this.peerId); this.appMessagesManager.togglePeerMute(this.peerId);
}, },
verify: () => this.chat.type === 'chat' && rootScope.myId !== this.peerId && this.appNotificationsManager.isPeerLocalMuted(this.peerId, false) verify: () => this.chat.type === 'chat' && rootScope.myId !== this.peerId && this.appNotificationsManager.isPeerLocalMuted(this.peerId, false)
}, { }, {
@ -544,9 +543,7 @@ export default class ChatTopbar {
this.openPinned(true); this.openPinned(true);
}); });
this.attachClickEvent(this.btnMute, () => { this.attachClickEvent(this.btnMute, this.onMuteClick);
this.appMessagesManager.mutePeer(this.peerId);
});
this.attachClickEvent(this.btnJoin, () => { this.attachClickEvent(this.btnJoin, () => {
const middleware = this.chat.bubbles.getMiddleware(); const middleware = this.chat.bubbles.getMiddleware();
@ -649,6 +646,10 @@ export default class ChatTopbar {
}); });
} }
private onMuteClick = () => {
new PopupMute(this.peerId);
};
private onResize = () => { private onResize = () => {
this.setUtilsWidth(true); this.setUtilsWidth(true);
this.setFloating(); this.setFloating();

5
src/components/dialogsContextMenu.ts

@ -18,6 +18,7 @@ import PopupPeer from "./popups/peer";
import AppChatFoldersTab from "./sidebarLeft/tabs/chatFolders"; import AppChatFoldersTab from "./sidebarLeft/tabs/chatFolders";
import appSidebarLeft from "./sidebarLeft"; import appSidebarLeft from "./sidebarLeft";
import { toastNew } from "./toast"; import { toastNew } from "./toast";
import PopupMute from "./popups/mute";
export default class DialogsContextMenu { export default class DialogsContextMenu {
private element: HTMLElement; private element: HTMLElement;
@ -123,11 +124,11 @@ export default class DialogsContextMenu {
}; };
private onUnmuteClick = () => { private onUnmuteClick = () => {
appMessagesManager.mutePeer(this.selectedId, false); appMessagesManager.togglePeerMute(this.selectedId, false);
}; };
private onMuteClick = () => { private onMuteClick = () => {
appMessagesManager.mutePeer(this.selectedId, true); new PopupMute(this.selectedId);
}; };
private onUnreadClick = () => { private onUnreadClick = () => {

2
src/components/peerProfile.ts

@ -156,7 +156,7 @@ export default class PeerProfile {
} }
//let checked = this.notificationsCheckbox.checked; //let checked = this.notificationsCheckbox.checked;
appMessagesManager.mutePeer(this.peerId); appMessagesManager.togglePeerMute(this.peerId);
}); });
rootScope.addEventListener('dialog_notify_settings', (dialog) => { rootScope.addEventListener('dialog_notify_settings', (dialog) => {

77
src/components/popups/mute.ts

@ -0,0 +1,77 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import tsNow from "../../helpers/tsNow";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import { LangPackKey } from "../../lib/langPack";
import { MUTE_UNTIL } from "../../lib/mtproto/mtproto_config";
import RadioField from "../radioField";
import Row, { RadioFormFromRows } from "../row";
import { SettingSection } from "../sidebarLeft";
import PopupPeer from "./peer";
export default class PopupMute extends PopupPeer {
constructor(peerId: PeerId) {
super('popup-mute', {
peerId,
titleLangKey: 'Notifications',
buttons: [{
langKey: 'ChatList.Context.Mute',
callback: () => {
appMessagesManager.mutePeer(peerId, time === -1 ? MUTE_UNTIL : tsNow(true) + time);
}
}],
body: true
});
const ONE_HOUR = 3600;
const times: {time: number, langKey: LangPackKey}[] = [{
time: ONE_HOUR,
langKey: 'ChatList.Mute.1Hour'
}, {
time: ONE_HOUR * 4,
langKey: 'ChatList.Mute.4Hours'
}, {
time: ONE_HOUR * 8,
langKey: 'ChatList.Mute.8Hours'
}, {
time: ONE_HOUR * 24,
langKey: 'ChatList.Mute.1Day'
}, {
time: ONE_HOUR * 24 * 3,
langKey: 'ChatList.Mute.3Days'
}, {
time: -1,
langKey: 'ChatList.Mute.Forever'
}];
const name = 'mute-time';
const rows = times.map((time) => {
const row = new Row({
radioField: new RadioField({
langKey: time.langKey,
name,
value: '' + time.time
})
});
return row;
});
let time: number;
const radioForm = RadioFormFromRows(rows, (value) => {
time = +value;
});
rows[rows.length - 1].radioField.checked = true;
const section = new SettingSection({noShadow: true, noDelimiter: true});
section.content.append(radioForm);
this.body.append(section.container);
this.show();
}
}

2
src/components/sidebarRight/tabs/editContact.ts

@ -90,7 +90,7 @@ export default class AppEditContactTab extends SliderSuperTab {
return; return;
} }
appMessagesManager.mutePeer(this.peerId); appMessagesManager.togglePeerMute(this.peerId);
}); });
this.listenerSetter.add(rootScope)('notify_settings', (update) => { this.listenerSetter.add(rootScope)('notify_settings', (update) => {

6
src/lang.ts

@ -814,6 +814,12 @@ const lang = {
"ChatList.Filter.Exclude.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.", "ChatList.Filter.Exclude.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.",
"ChatList.Filter.Confirm.Remove.Header": "Remove Folder", "ChatList.Filter.Confirm.Remove.Header": "Remove Folder",
"ChatList.Filter.Confirm.Remove.Text": "Are you sure you want to remove this folder? Your chats will not be deleted.", "ChatList.Filter.Confirm.Remove.Text": "Are you sure you want to remove this folder? Your chats will not be deleted.",
"ChatList.Mute.1Hour": "For 1 Hour",
"ChatList.Mute.4Hours": "For 4 Hours",
"ChatList.Mute.8Hours": "For 8 Hours",
"ChatList.Mute.1Day": "For 1 Day",
"ChatList.Mute.3Days": "For 3 Days",
"ChatList.Mute.Forever": "Forever",
"Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.", "Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.",
"CreateGroup.NameHolder": "Group Name", "CreateGroup.NameHolder": "Group Name",
"Date.Today": "Today", "Date.Today": "Today",

16
src/lib/appManagers/appMessagesManager.ts

@ -5127,16 +5127,12 @@ export class AppMessagesManager {
return pendingMessage; return pendingMessage;
} }
public mutePeer(peerId: PeerId, mute?: boolean) { public mutePeer(peerId: PeerId, muteUntil: number) {
const settings: InputPeerNotifySettings = { const settings: InputPeerNotifySettings = {
_: 'inputPeerNotifySettings' _: 'inputPeerNotifySettings'
}; };
if(mute === undefined) { settings.mute_until = muteUntil;
mute = !appNotificationsManager.isPeerLocalMuted(peerId, false);
}
settings.mute_until = mute ? MUTE_UNTIL : 0;
return appNotificationsManager.updateNotifySettings({ return appNotificationsManager.updateNotifySettings({
_: 'inputNotifyPeer', _: 'inputNotifyPeer',
@ -5144,6 +5140,14 @@ export class AppMessagesManager {
}, settings); }, settings);
} }
public togglePeerMute(peerId: PeerId, mute?: boolean) {
if(mute === undefined) {
mute = !appNotificationsManager.isPeerLocalMuted(peerId, false);
}
return this.mutePeer(peerId, mute ? MUTE_UNTIL : 0);
}
public canSendToPeer(peerId: PeerId, threadId?: number, action: ChatRights = 'send_messages') { public canSendToPeer(peerId: PeerId, threadId?: number, action: ChatRights = 'send_messages') {
if(peerId.isAnyChat()) { if(peerId.isAnyChat()) {
//const isChannel = appPeersManager.isChannel(peerId); //const isChannel = appPeersManager.isChannel(peerId);

50
src/lib/appManagers/appNotificationsManager.ts

@ -29,6 +29,8 @@ import appRuntimeManager from "./appRuntimeManager";
import appStateManager from "./appStateManager"; import appStateManager from "./appStateManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
import IS_VIBRATE_SUPPORTED from "../../environment/vibrateSupport"; import IS_VIBRATE_SUPPORTED from "../../environment/vibrateSupport";
import { MUTE_UNTIL } from "../mtproto/mtproto_config";
import throttle from "../../helpers/schedulers/throttle";
type MyNotification = Notification & { type MyNotification = Notification & {
hidden?: boolean, hidden?: boolean,
@ -91,6 +93,9 @@ export class AppNotificationsManager {
private getNotifyPeerTypePromise: Promise<any>; private getNotifyPeerTypePromise: Promise<any>;
private checkMuteUntilTimeout: number;
private checkMuteUntilThrottled: () => void;
constructor() { constructor() {
// @ts-ignore // @ts-ignore
navigator.vibrate = navigator.vibrate || navigator.mozVibrate || navigator.webkitVibrate; navigator.vibrate = navigator.vibrate || navigator.mozVibrate || navigator.webkitVibrate;
@ -103,6 +108,8 @@ export class AppNotificationsManager {
this.notifySoundEl.id = 'notify-sound'; this.notifySoundEl.id = 'notify-sound';
document.body.append(this.notifySoundEl); document.body.append(this.notifySoundEl);
this.checkMuteUntilThrottled = throttle(this.checkMuteUntil, 1000, false);
rootScope.addEventListener('instance_deactivated', () => { rootScope.addEventListener('instance_deactivated', () => {
this.stop(); this.stop();
}); });
@ -414,6 +421,45 @@ export class AppNotificationsManager {
this.prevFavicon = href; this.prevFavicon = href;
} }
private checkMuteUntil = () => {
if(this.checkMuteUntilTimeout !== undefined) {
clearTimeout(this.checkMuteUntilTimeout);
this.checkMuteUntilTimeout = undefined;
}
const timestamp = tsNow(true);
let closestMuteUntil = MUTE_UNTIL;
for(const peerId in this.peerSettings.notifyPeer) {
const peerNotifySettings = this.peerSettings.notifyPeer[peerId];
if(peerNotifySettings instanceof Promise) {
continue;
}
const muteUntil = peerNotifySettings.mute_until;
if(muteUntil === undefined) {
continue;
}
if(muteUntil <= timestamp) {
// ! do not delete it because peer's unique settings will be overwritten in getPeerLocalSettings with type's settings
// delete peerNotifySettings.mute_until;
rootScope.dispatchEvent('updateNotifySettings', {
_: 'updateNotifySettings',
peer: {
_: 'notifyPeer',
peer: appPeersManager.getOutputPeer(peerId.toPeerId())
},
notify_settings: peerNotifySettings
});
} else if(muteUntil < closestMuteUntil) {
closestMuteUntil = muteUntil;
}
}
this.checkMuteUntilTimeout = window.setTimeout(this.checkMuteUntil, (closestMuteUntil - timestamp) * 1000);
};
public savePeerSettings({key, peerId, settings}: { public savePeerSettings({key, peerId, settings}: {
key?: Exclude<NotifyPeer['_'], 'notifyPeer'>, key?: Exclude<NotifyPeer['_'], 'notifyPeer'>,
peerId?: PeerId, peerId?: PeerId,
@ -429,6 +475,8 @@ export class AppNotificationsManager {
if(!peerId) { if(!peerId) {
rootScope.dispatchEvent('notify_peer_type_settings', {key, settings}); rootScope.dispatchEvent('notify_peer_type_settings', {key, settings});
} else {
this.checkMuteUntilThrottled();
} }
//rootScope.broadcast('notify_settings', {peerId: peerId}); //rootScope.broadcast('notify_settings', {peerId: peerId});
@ -436,7 +484,7 @@ export class AppNotificationsManager {
public isMuted(peerNotifySettings: PeerNotifySettings) { public isMuted(peerNotifySettings: PeerNotifySettings) {
return peerNotifySettings._ === 'peerNotifySettings' && return peerNotifySettings._ === 'peerNotifySettings' &&
((peerNotifySettings.mute_until * 1000) > tsNow() || peerNotifySettings.silent); (peerNotifySettings.silent || (peerNotifySettings.mute_until !== undefined && (peerNotifySettings.mute_until * 1000) > tsNow()));
} }
public getPeerMuted(peerId: PeerId) { public getPeerMuted(peerId: PeerId) {

24
src/scss/partials/popups/_mute.scss

@ -0,0 +1,24 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.popup-mute {
.popup-container {
width: 16rem;
}
.popup-body {
margin: 0 -.625rem;
}
.sidebar-left-section {
margin-bottom: 0 !important;
padding: 0 !important;
&-content {
margin: 0 !important;
}
}
}

1
src/scss/style.scss

@ -321,6 +321,7 @@ $chat-input-inner-padding-handhelds: .25rem;
@import "partials/popups/groupCall"; @import "partials/popups/groupCall";
@import "partials/popups/call"; @import "partials/popups/call";
@import "partials/popups/sponsored"; @import "partials/popups/sponsored";
@import "partials/popups/mute";
@import "partials/pages/pages"; @import "partials/pages/pages";
@import "partials/pages/authCode"; @import "partials/pages/authCode";

Loading…
Cancel
Save