Eduard Kuzmenko
4 years ago
46 changed files with 1770 additions and 427 deletions
@ -1,78 +0,0 @@ |
|||||||
import appStateManager from "../lib/appManagers/appStateManager"; |
|
||||||
import { getDeepProperty } from "../helpers/object"; |
|
||||||
|
|
||||||
const CheckboxField = (options: { |
|
||||||
text?: string, |
|
||||||
name?: string, |
|
||||||
round?: boolean, |
|
||||||
stateKey?: string, |
|
||||||
disabled?: boolean |
|
||||||
} = {}) => { |
|
||||||
const label = document.createElement('label'); |
|
||||||
label.classList.add('checkbox-field'); |
|
||||||
|
|
||||||
if(options.round) { |
|
||||||
label.classList.add('checkbox-field-round'); |
|
||||||
} |
|
||||||
|
|
||||||
if(options.disabled) { |
|
||||||
label.classList.add('checkbox-disabled'); |
|
||||||
} |
|
||||||
|
|
||||||
const input = document.createElement('input'); |
|
||||||
input.type = 'checkbox'; |
|
||||||
if(options.name) { |
|
||||||
input.id = 'input-' + name; |
|
||||||
} |
|
||||||
|
|
||||||
if(options.stateKey) { |
|
||||||
appStateManager.getState().then(state => { |
|
||||||
input.checked = getDeepProperty(state, options.stateKey); |
|
||||||
}); |
|
||||||
|
|
||||||
input.addEventListener('change', () => { |
|
||||||
appStateManager.setByKey(options.stateKey, input.checked); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
let span: HTMLSpanElement; |
|
||||||
if(options.text) { |
|
||||||
span = document.createElement('span'); |
|
||||||
span.classList.add('checkbox-caption'); |
|
||||||
|
|
||||||
if(options.text) { |
|
||||||
span.innerText = options.text; |
|
||||||
} |
|
||||||
} else { |
|
||||||
label.classList.add('checkbox-without-caption'); |
|
||||||
} |
|
||||||
|
|
||||||
const box = document.createElement('div'); |
|
||||||
box.classList.add('checkbox-box'); |
|
||||||
|
|
||||||
const checkSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); |
|
||||||
checkSvg.classList.add('checkbox-box-check'); |
|
||||||
checkSvg.setAttributeNS(null, 'viewBox', '0 0 24 24'); |
|
||||||
const use = document.createElementNS("http://www.w3.org/2000/svg", "use"); |
|
||||||
use.setAttributeNS(null, 'href', '#check'); |
|
||||||
use.setAttributeNS(null, 'x', '-1'); |
|
||||||
checkSvg.append(use); |
|
||||||
|
|
||||||
const bg = document.createElement('div'); |
|
||||||
bg.classList.add('checkbox-box-background'); |
|
||||||
|
|
||||||
const border = document.createElement('div'); |
|
||||||
border.classList.add('checkbox-box-border'); |
|
||||||
|
|
||||||
box.append(border, bg, checkSvg); |
|
||||||
|
|
||||||
label.append(input, box); |
|
||||||
|
|
||||||
if(span) { |
|
||||||
label.append(span); |
|
||||||
} |
|
||||||
|
|
||||||
return {label, input, span}; |
|
||||||
}; |
|
||||||
|
|
||||||
export default CheckboxField; |
|
@ -0,0 +1,100 @@ |
|||||||
|
import appStateManager from "../lib/appManagers/appStateManager"; |
||||||
|
import { getDeepProperty } from "../helpers/object"; |
||||||
|
|
||||||
|
export default class CheckboxField { |
||||||
|
public input: HTMLInputElement; |
||||||
|
public label: HTMLLabelElement; |
||||||
|
public span: HTMLSpanElement; |
||||||
|
|
||||||
|
constructor(options: { |
||||||
|
text?: string, |
||||||
|
name?: string, |
||||||
|
round?: boolean, |
||||||
|
stateKey?: string, |
||||||
|
disabled?: boolean, |
||||||
|
checked?: boolean, |
||||||
|
} = {}) { |
||||||
|
const label = this.label = document.createElement('label'); |
||||||
|
label.classList.add('checkbox-field'); |
||||||
|
|
||||||
|
if(options.round) { |
||||||
|
label.classList.add('checkbox-field-round'); |
||||||
|
} |
||||||
|
|
||||||
|
if(options.disabled) { |
||||||
|
label.classList.add('checkbox-disabled'); |
||||||
|
} |
||||||
|
|
||||||
|
const input = this.input = document.createElement('input'); |
||||||
|
input.type = 'checkbox'; |
||||||
|
if(options.name) { |
||||||
|
input.id = 'input-' + name; |
||||||
|
} |
||||||
|
|
||||||
|
if(options.checked) { |
||||||
|
input.checked = true; |
||||||
|
} |
||||||
|
|
||||||
|
if(options.stateKey) { |
||||||
|
appStateManager.getState().then(state => { |
||||||
|
this.value = getDeepProperty(state, options.stateKey); |
||||||
|
|
||||||
|
input.addEventListener('change', () => { |
||||||
|
appStateManager.setByKey(options.stateKey, input.checked); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
let span: HTMLSpanElement; |
||||||
|
if(options.text) { |
||||||
|
span = this.span = document.createElement('span'); |
||||||
|
span.classList.add('checkbox-caption'); |
||||||
|
|
||||||
|
if(options.text) { |
||||||
|
span.innerText = options.text; |
||||||
|
} |
||||||
|
} else { |
||||||
|
label.classList.add('checkbox-without-caption'); |
||||||
|
} |
||||||
|
|
||||||
|
const box = document.createElement('div'); |
||||||
|
box.classList.add('checkbox-box'); |
||||||
|
|
||||||
|
const checkSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); |
||||||
|
checkSvg.classList.add('checkbox-box-check'); |
||||||
|
checkSvg.setAttributeNS(null, 'viewBox', '0 0 24 24'); |
||||||
|
const use = document.createElementNS("http://www.w3.org/2000/svg", "use"); |
||||||
|
use.setAttributeNS(null, 'href', '#check'); |
||||||
|
use.setAttributeNS(null, 'x', '-1'); |
||||||
|
checkSvg.append(use); |
||||||
|
|
||||||
|
const bg = document.createElement('div'); |
||||||
|
bg.classList.add('checkbox-box-background'); |
||||||
|
|
||||||
|
const border = document.createElement('div'); |
||||||
|
border.classList.add('checkbox-box-border'); |
||||||
|
|
||||||
|
box.append(border, bg, checkSvg); |
||||||
|
|
||||||
|
label.append(input, box); |
||||||
|
|
||||||
|
if(span) { |
||||||
|
label.append(span); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
get value() { |
||||||
|
return this.input.checked; |
||||||
|
} |
||||||
|
|
||||||
|
set value(value: boolean) { |
||||||
|
this.setValueSilently(value); |
||||||
|
|
||||||
|
const event = new Event('change', {bubbles: true, cancelable: true}); |
||||||
|
this.input.dispatchEvent(event); |
||||||
|
} |
||||||
|
|
||||||
|
public setValueSilently(value: boolean) { |
||||||
|
this.input.checked = value; |
||||||
|
} |
||||||
|
} |
@ -1,48 +1,50 @@ |
|||||||
import appStateManager from "../lib/appManagers/appStateManager"; |
import appStateManager from "../lib/appManagers/appStateManager"; |
||||||
import { getDeepProperty } from "../helpers/object"; |
import { getDeepProperty } from "../helpers/object"; |
||||||
|
|
||||||
const RadioField = (text: string, name: string, value?: string, stateKey?: string) => { |
export default class RadioField { |
||||||
const label = document.createElement('label'); |
public input: HTMLInputElement; |
||||||
label.classList.add('radio-field'); |
public label: HTMLLabelElement; |
||||||
|
public main: HTMLElement; |
||||||
const input = document.createElement('input'); |
|
||||||
input.type = 'radio'; |
constructor(text: string, name: string, value?: string, stateKey?: string) { |
||||||
/* input.id = */input.name = 'input-radio-' + name; |
const label = this.label = document.createElement('label'); |
||||||
|
label.classList.add('radio-field'); |
||||||
if(value) { |
|
||||||
input.value = value; |
const input = this.input = document.createElement('input'); |
||||||
|
input.type = 'radio'; |
||||||
if(stateKey) { |
/* input.id = */input.name = 'input-radio-' + name; |
||||||
appStateManager.getState().then(state => { |
|
||||||
input.checked = getDeepProperty(state, stateKey) === value; |
if(value) { |
||||||
}); |
input.value = value; |
||||||
|
|
||||||
input.addEventListener('change', () => { |
if(stateKey) { |
||||||
appStateManager.setByKey(stateKey, value); |
appStateManager.getState().then(state => { |
||||||
}); |
input.checked = getDeepProperty(state, stateKey) === value; |
||||||
|
}); |
||||||
|
|
||||||
|
input.addEventListener('change', () => { |
||||||
|
appStateManager.setByKey(stateKey, value); |
||||||
|
}); |
||||||
|
} |
||||||
} |
} |
||||||
} |
|
||||||
|
const main = this.main = document.createElement('div'); |
||||||
const main = document.createElement('div'); |
main.classList.add('radio-field-main'); |
||||||
main.classList.add('radio-field-main'); |
|
||||||
|
if(text) { |
||||||
if(text) { |
main.innerHTML = text; |
||||||
main.innerHTML = text; |
/* const caption = document.createElement('div'); |
||||||
/* const caption = document.createElement('div'); |
caption.classList.add('radio-field-main-caption'); |
||||||
caption.classList.add('radio-field-main-caption'); |
caption.innerHTML = text; |
||||||
caption.innerHTML = text; |
|
||||||
|
if(subtitle) { |
||||||
if(subtitle) { |
label.classList.add('radio-field-with-subtitle'); |
||||||
label.classList.add('radio-field-with-subtitle'); |
caption.insertAdjacentHTML('beforeend', `<div class="radio-field-main-subtitle">${subtitle}</div>`); |
||||||
caption.insertAdjacentHTML('beforeend', `<div class="radio-field-main-subtitle">${subtitle}</div>`); |
} |
||||||
|
|
||||||
|
main.append(caption); */ |
||||||
} |
} |
||||||
|
|
||||||
main.append(caption); */ |
label.append(input, main); |
||||||
} |
} |
||||||
|
|
||||||
label.append(input, main); |
|
||||||
|
|
||||||
return {label, input, main}; |
|
||||||
}; |
}; |
||||||
|
|
||||||
export default RadioField; |
|
@ -0,0 +1,128 @@ |
|||||||
|
import { SettingSection } from ".."; |
||||||
|
import Row from "../../row"; |
||||||
|
import CheckboxField from "../../checkboxField"; |
||||||
|
import { InputNotifyPeer, PeerNotifySettings, Update } from "../../../layer"; |
||||||
|
import appNotificationsManager from "../../../lib/appManagers/appNotificationsManager"; |
||||||
|
import { SliderSuperTabEventable } from "../../sliderTab"; |
||||||
|
import { copy } from "../../../helpers/object"; |
||||||
|
import rootScope from "../../../lib/rootScope"; |
||||||
|
import { convertKeyToInputKey } from "../../../helpers/string"; |
||||||
|
|
||||||
|
type InputNotifyKey = Exclude<InputNotifyPeer['_'], 'inputNotifyPeer'>; |
||||||
|
|
||||||
|
export default class AppNotificationsTab extends SliderSuperTabEventable { |
||||||
|
protected init() { |
||||||
|
this.container.classList.add('notifications-container'); |
||||||
|
this.title.innerText = 'Notifications'; |
||||||
|
|
||||||
|
const NotifySection = (options: { |
||||||
|
name: string, |
||||||
|
typeText: string, |
||||||
|
inputKey: InputNotifyKey, |
||||||
|
}) => { |
||||||
|
const section = new SettingSection({ |
||||||
|
name: options.name |
||||||
|
}); |
||||||
|
|
||||||
|
const enabledRow = new Row({ |
||||||
|
checkboxField: new CheckboxField({text: options.typeText, checked: true}), |
||||||
|
subtitle: 'Loading...', |
||||||
|
}); |
||||||
|
|
||||||
|
const previewEnabledRow = new Row({ |
||||||
|
checkboxField: new CheckboxField({text: 'Message preview', checked: true}), |
||||||
|
subtitle: 'Loading...', |
||||||
|
}); |
||||||
|
|
||||||
|
section.content.append(enabledRow.container, previewEnabledRow.container); |
||||||
|
|
||||||
|
this.scrollable.append(section.container); |
||||||
|
|
||||||
|
const inputNotifyPeer = {_: options.inputKey}; |
||||||
|
appNotificationsManager.getNotifySettings(inputNotifyPeer).then((notifySettings) => { |
||||||
|
const applySettings = () => { |
||||||
|
const muted = appNotificationsManager.isMuted(notifySettings); |
||||||
|
enabledRow.checkboxField.value = !muted; |
||||||
|
previewEnabledRow.checkboxField.value = notifySettings.show_previews; |
||||||
|
|
||||||
|
return muted; |
||||||
|
}; |
||||||
|
|
||||||
|
applySettings(); |
||||||
|
|
||||||
|
this.eventListener.addListener('destroy', () => { |
||||||
|
const mute = !enabledRow.checkboxField.value; |
||||||
|
const showPreviews = previewEnabledRow.checkboxField.value; |
||||||
|
|
||||||
|
if(mute === appNotificationsManager.isMuted(notifySettings) && showPreviews === notifySettings.show_previews) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const inputSettings: any = copy(notifySettings); |
||||||
|
inputSettings._ = 'inputPeerNotifySettings'; |
||||||
|
inputSettings.mute_until = mute ? 2147483647 : 0; |
||||||
|
inputSettings.show_previews = showPreviews; |
||||||
|
|
||||||
|
appNotificationsManager.updateNotifySettings(inputNotifyPeer, inputSettings); |
||||||
|
}, true); |
||||||
|
|
||||||
|
this.listenerSetter.add(rootScope, 'notify_settings', (update: Update.updateNotifySettings) => { |
||||||
|
const inputKey = convertKeyToInputKey(update.peer._) as any; |
||||||
|
if(options.inputKey === inputKey) { |
||||||
|
notifySettings = update.notify_settings; |
||||||
|
applySettings(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
NotifySection({ |
||||||
|
name: 'Private Chats', |
||||||
|
typeText: 'Notifications for private chats', |
||||||
|
inputKey: 'inputNotifyUsers' |
||||||
|
}); |
||||||
|
|
||||||
|
NotifySection({ |
||||||
|
name: 'Groups', |
||||||
|
typeText: 'Notifications for groups', |
||||||
|
inputKey: 'inputNotifyChats' |
||||||
|
}); |
||||||
|
|
||||||
|
NotifySection({ |
||||||
|
name: 'Channels', |
||||||
|
typeText: 'Notifications for channels', |
||||||
|
inputKey: 'inputNotifyBroadcasts' |
||||||
|
}); |
||||||
|
|
||||||
|
{ |
||||||
|
const section = new SettingSection({ |
||||||
|
name: 'Other' |
||||||
|
}); |
||||||
|
|
||||||
|
const contactsSignUpRow = new Row({ |
||||||
|
checkboxField: new CheckboxField({text: 'Contacts joined Telegram', checked: true}), |
||||||
|
subtitle: 'Loading...', |
||||||
|
}); |
||||||
|
|
||||||
|
const soundRow = new Row({ |
||||||
|
checkboxField: new CheckboxField({text: 'Notification sound', checked: true, stateKey: 'settings.notifications.sound'}), |
||||||
|
subtitle: 'Enabled', |
||||||
|
}); |
||||||
|
|
||||||
|
section.content.append(contactsSignUpRow.container, soundRow.container); |
||||||
|
|
||||||
|
this.scrollable.append(section.container); |
||||||
|
|
||||||
|
appNotificationsManager.getContactSignUpNotification().then(enabled => { |
||||||
|
contactsSignUpRow.checkboxField.value = enabled; |
||||||
|
|
||||||
|
this.eventListener.addListener('destroy', () => { |
||||||
|
const _enabled = contactsSignUpRow.checkboxField.value; |
||||||
|
if(enabled !== _enabled) { |
||||||
|
appNotificationsManager.setContactSignUpNotification(!_enabled); |
||||||
|
} |
||||||
|
}, true); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,675 @@ |
|||||||
|
import { fontFamily } from "../../components/middleEllipsis"; |
||||||
|
import { MOUNT_CLASS_TO } from "../../config/debug"; |
||||||
|
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise"; |
||||||
|
import { tsNow } from "../../helpers/date"; |
||||||
|
import { copy, deepEqual } from "../../helpers/object"; |
||||||
|
import { convertInputKeyToKey } from "../../helpers/string"; |
||||||
|
import { isMobile } from "../../helpers/userAgent"; |
||||||
|
import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from "../../layer"; |
||||||
|
import Config from "../config"; |
||||||
|
import apiManager from "../mtproto/mtprotoworker"; |
||||||
|
import rootScope from "../rootScope"; |
||||||
|
import sessionStorage from "../sessionStorage"; |
||||||
|
import apiUpdatesManager from "./apiUpdatesManager"; |
||||||
|
import appChatsManager from "./appChatsManager"; |
||||||
|
import appPeersManager from "./appPeersManager"; |
||||||
|
import appStateManager from "./appStateManager"; |
||||||
|
import appUsersManager from "./appUsersManager"; |
||||||
|
|
||||||
|
type MyNotification = Notification & { |
||||||
|
hidden?: boolean, |
||||||
|
show?: () => void, |
||||||
|
}; |
||||||
|
|
||||||
|
export type NotifyOptions = Partial<{ |
||||||
|
tag: string; |
||||||
|
image: string; |
||||||
|
key: string; |
||||||
|
title: string; |
||||||
|
message: string; |
||||||
|
silent: boolean; |
||||||
|
onclick: () => void; |
||||||
|
}>; |
||||||
|
|
||||||
|
export class AppNotificationsManager { |
||||||
|
private notificationsUiSupport: boolean; |
||||||
|
private notificationsShown: {[key: string]: MyNotification} = {}; |
||||||
|
private notificationIndex = 0; |
||||||
|
private notificationsCount = 0; |
||||||
|
private soundsPlayed: {[tag: string]: number} = {}; |
||||||
|
private vibrateSupport = !!navigator.vibrate; |
||||||
|
private nextSoundAt: number; |
||||||
|
private prevSoundVolume: number; |
||||||
|
private peerSettings = { |
||||||
|
notifyPeer: {} as {[peerId: number]: Promise<PeerNotifySettings>}, |
||||||
|
notifyUsers: null as Promise<PeerNotifySettings>, |
||||||
|
notifyChats: null as Promise<PeerNotifySettings>, |
||||||
|
notifyBroadcasts: null as Promise<PeerNotifySettings> |
||||||
|
}; |
||||||
|
private exceptions: {[peerId: string]: PeerNotifySettings} = {}; |
||||||
|
private notifyContactsSignUp: Promise<boolean>; |
||||||
|
private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel="icon"]'); |
||||||
|
private langNotificationsPluralize = 'notifications';//_.pluralize('page_title_pluralize_notifications');
|
||||||
|
|
||||||
|
private titleBackup = document.title; |
||||||
|
private titleChanged = false; |
||||||
|
private titleInterval: number; |
||||||
|
private prevFavicon: string; |
||||||
|
private stopped = false; |
||||||
|
|
||||||
|
private settings: Partial<{ |
||||||
|
nodesktop: boolean, |
||||||
|
volume: number, |
||||||
|
novibrate: boolean, |
||||||
|
nopreview: boolean, |
||||||
|
nopush: boolean, |
||||||
|
nosound: boolean, |
||||||
|
}> = {}; |
||||||
|
|
||||||
|
private registeredDevice: any; |
||||||
|
private pushInited = false; |
||||||
|
|
||||||
|
private topMessagesDeferred: CancellablePromise<void>; |
||||||
|
|
||||||
|
private notifySoundEl: HTMLElement; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
// @ts-ignore
|
||||||
|
navigator.vibrate = navigator.vibrate || navigator.mozVibrate || navigator.webkitVibrate; |
||||||
|
|
||||||
|
this.notificationsUiSupport = ('Notification' in window) || ('mozNotification' in navigator); |
||||||
|
|
||||||
|
this.topMessagesDeferred = deferredPromise<void>(); |
||||||
|
|
||||||
|
this.notifySoundEl = document.createElement('div'); |
||||||
|
this.notifySoundEl.id = 'notify-sound'; |
||||||
|
document.body.append(this.notifySoundEl); |
||||||
|
|
||||||
|
/* rootScope.on('idle.deactivated', (newVal) => { |
||||||
|
if(newVal) { |
||||||
|
stop(); |
||||||
|
} |
||||||
|
});*/ |
||||||
|
|
||||||
|
rootScope.on('idle', (newVal) => { |
||||||
|
if(this.stopped) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(!newVal) { |
||||||
|
this.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
this.toggleToggler(); |
||||||
|
}); |
||||||
|
|
||||||
|
rootScope.on('apiUpdate', (update) => { |
||||||
|
// console.log('on apiUpdate', update)
|
||||||
|
switch(update._) { |
||||||
|
case 'updateNotifySettings': { |
||||||
|
this.savePeerSettings(update.peer._ === 'notifyPeer' ? appPeersManager.getPeerId(update.peer.peer) : update.peer._, update.notify_settings); |
||||||
|
rootScope.broadcast('notify_settings', update); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
/* rootScope.on('push_init', (tokenData) => { |
||||||
|
this.pushInited = true |
||||||
|
if(!this.settings.nodesktop && !this.settings.nopush) { |
||||||
|
if(tokenData) { |
||||||
|
this.registerDevice(tokenData); |
||||||
|
} else { |
||||||
|
WebPushApiManager.subscribe(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
this.unregisterDevice(tokenData); |
||||||
|
} |
||||||
|
}); |
||||||
|
rootScope.on('push_subscribe', (tokenData) => { |
||||||
|
this.registerDevice(tokenData); |
||||||
|
}); |
||||||
|
rootScope.on('push_unsubscribe', (tokenData) => { |
||||||
|
this.unregisterDevice(tokenData); |
||||||
|
}); */ |
||||||
|
|
||||||
|
rootScope.addListener('dialogs_multiupdate', () => { |
||||||
|
//unregisterTopMsgs()
|
||||||
|
this.topMessagesDeferred.resolve(); |
||||||
|
}, true); |
||||||
|
|
||||||
|
/* rootScope.on('push_notification_click', (notificationData) => { |
||||||
|
if(notificationData.action === 'push_settings') { |
||||||
|
this.topMessagesDeferred.then(() => { |
||||||
|
$modal.open({ |
||||||
|
templateUrl: templateUrl('settings_modal'), |
||||||
|
controller: 'SettingsModalController', |
||||||
|
windowClass: 'settings_modal_window mobile_modal', |
||||||
|
backdrop: 'single' |
||||||
|
}) |
||||||
|
}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(notificationData.action === 'mute1d') { |
||||||
|
apiManager.invokeApi('account.updateDeviceLocked', { |
||||||
|
period: 86400 |
||||||
|
}).then(() => { |
||||||
|
// var toastData = toaster.pop({
|
||||||
|
// type: 'info',
|
||||||
|
// body: _('push_action_mute1d_success'),
|
||||||
|
// bodyOutputType: 'trustedHtml',
|
||||||
|
// clickHandler: () => {
|
||||||
|
// toaster.clear(toastData)
|
||||||
|
// },
|
||||||
|
// showCloseButton: false
|
||||||
|
// })
|
||||||
|
}); |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const peerId = notificationData.custom && notificationData.custom.peerId; |
||||||
|
console.log('click', notificationData, peerId); |
||||||
|
if(peerId) { |
||||||
|
this.topMessagesDeferred.then(() => { |
||||||
|
if(notificationData.custom.channel_id && |
||||||
|
!appChatsManager.hasChat(notificationData.custom.channel_id)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(peerId > 0 && !appUsersManager.hasUser(peerId)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// rootScope.broadcast('history_focus', {
|
||||||
|
// peerString: appPeersManager.getPeerString(peerId)
|
||||||
|
// });
|
||||||
|
}); |
||||||
|
} |
||||||
|
}); */ |
||||||
|
} |
||||||
|
|
||||||
|
private toggleToggler(enable = rootScope.idle.isIDLE) { |
||||||
|
if(isMobile) return; |
||||||
|
|
||||||
|
const resetTitle = () => { |
||||||
|
this.titleChanged = false; |
||||||
|
document.title = this.titleBackup; |
||||||
|
this.setFavicon(); |
||||||
|
}; |
||||||
|
|
||||||
|
window.clearInterval(this.titleInterval); |
||||||
|
this.titleInterval = 0; |
||||||
|
|
||||||
|
if(!enable) { |
||||||
|
resetTitle(); |
||||||
|
} else { |
||||||
|
this.titleInterval = window.setInterval(() => { |
||||||
|
if(!this.notificationsCount) { |
||||||
|
this.toggleToggler(false); |
||||||
|
} else if(this.titleChanged) { |
||||||
|
resetTitle(); |
||||||
|
} else { |
||||||
|
this.titleChanged = true; |
||||||
|
document.title = this.notificationsCount + ' ' + this.langNotificationsPluralize; |
||||||
|
//this.setFavicon('assets/img/favicon_unread.ico');
|
||||||
|
|
||||||
|
// fetch('assets/img/favicon.ico')
|
||||||
|
// .then(res => res.blob())
|
||||||
|
// .then(blob => {
|
||||||
|
// const img = document.createElement('img');
|
||||||
|
// img.src = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas'); |
||||||
|
canvas.width = 32 * window.devicePixelRatio; |
||||||
|
canvas.height = canvas.width; |
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d'); |
||||||
|
ctx.beginPath(); |
||||||
|
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, 2 * Math.PI, false); |
||||||
|
ctx.fillStyle = '#5b8af1'; |
||||||
|
ctx.fill(); |
||||||
|
|
||||||
|
let fontSize = 24; |
||||||
|
let str = '' + this.notificationsCount; |
||||||
|
if(this.notificationsCount < 10) { |
||||||
|
fontSize = 22; |
||||||
|
} else if(this.notificationsCount < 100) { |
||||||
|
fontSize = 20; |
||||||
|
} else { |
||||||
|
str = '99+'; |
||||||
|
fontSize = 18; |
||||||
|
} |
||||||
|
|
||||||
|
ctx.font = `700 ${fontSize}px ${fontFamily}`; |
||||||
|
ctx.textBaseline = 'middle'; |
||||||
|
ctx.textAlign = 'center'; |
||||||
|
ctx.fillStyle = 'white'; |
||||||
|
ctx.fillText('' + this.notificationsCount, canvas.width / 2, canvas.height * .5625); |
||||||
|
|
||||||
|
/* const ctx = canvas.getContext('2d'); |
||||||
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); */ |
||||||
|
|
||||||
|
this.setFavicon(canvas.toDataURL()); |
||||||
|
// });
|
||||||
|
} |
||||||
|
}, 1000); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public updateLocalSettings() { |
||||||
|
Promise.all(['notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview', 'notify_nopush'].map(k => sessionStorage.get(k as any))) |
||||||
|
.then((updSettings) => { |
||||||
|
this.settings.nodesktop = updSettings[0]; |
||||||
|
this.settings.volume = updSettings[1] === undefined ? 0.5 : updSettings[1]; |
||||||
|
this.settings.novibrate = updSettings[2]; |
||||||
|
this.settings.nopreview = updSettings[3]; |
||||||
|
this.settings.nopush = updSettings[4]; |
||||||
|
|
||||||
|
/* if(this.pushInited) { |
||||||
|
const needPush = !this.settings.nopush && !this.settings.nodesktop && WebPushApiManager.isAvailable || false; |
||||||
|
const hasPush = this.registeredDevice !== false; |
||||||
|
if(needPush !== hasPush) { |
||||||
|
if(needPush) { |
||||||
|
WebPushApiManager.subscribe(); |
||||||
|
} else { |
||||||
|
WebPushApiManager.unsubscribe(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
WebPushApiManager.setSettings(this.settings); */ |
||||||
|
}); |
||||||
|
|
||||||
|
appStateManager.getState().then(state => { |
||||||
|
this.settings.nosound = !state.settings.notifications.sound; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public getLocalSettings() { |
||||||
|
return this.settings; |
||||||
|
} |
||||||
|
|
||||||
|
public getNotifySettings(peer: InputNotifyPeer): Promise<PeerNotifySettings> { |
||||||
|
let key: any = convertInputKeyToKey(peer._); |
||||||
|
let obj: any = this.peerSettings[key as NotifyPeer['_']]; |
||||||
|
|
||||||
|
if(peer._ === 'inputNotifyPeer') { |
||||||
|
key = appPeersManager.getPeerId(peer.peer); |
||||||
|
obj = obj[key]; |
||||||
|
} |
||||||
|
|
||||||
|
if(obj) { |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
return (obj || this.peerSettings)[key] = apiManager.invokeApi('account.getNotifySettings', {peer})/* .then(settings => { |
||||||
|
return settings; |
||||||
|
}) */; |
||||||
|
} |
||||||
|
|
||||||
|
public updateNotifySettings(peer: InputNotifyPeer, settings: InputPeerNotifySettings) { |
||||||
|
//this.savePeerSettings(peerId, settings);
|
||||||
|
|
||||||
|
/* const inputSettings: InputPeerNotifySettings = copy(settings) as any; |
||||||
|
inputSettings._ = 'inputPeerNotifySettings'; */ |
||||||
|
|
||||||
|
return apiManager.invokeApi('account.updateNotifySettings', { |
||||||
|
peer, |
||||||
|
settings |
||||||
|
}).then(value => { |
||||||
|
if(value) { |
||||||
|
apiUpdatesManager.processUpdateMessage({ |
||||||
|
_: 'updateShort', |
||||||
|
update: { |
||||||
|
_: 'updateNotifySettings', |
||||||
|
peer: { |
||||||
|
...peer, |
||||||
|
_: convertInputKeyToKey(peer._) |
||||||
|
}, |
||||||
|
notify_settings: { // ! WOW, IT WORKS !
|
||||||
|
...settings, |
||||||
|
_: 'peerNotifySettings', |
||||||
|
} |
||||||
|
} as Update.updateNotifySettings |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public getNotifyExceptions() { |
||||||
|
apiManager.invokeApi('account.getNotifyExceptions', {compare_sound: true}) |
||||||
|
.then((updates) => { |
||||||
|
apiUpdatesManager.processUpdateMessage(updates); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public getContactSignUpNotification() { |
||||||
|
if(this.notifyContactsSignUp) return this.notifyContactsSignUp; |
||||||
|
return this.notifyContactsSignUp = apiManager.invokeApi('account.getContactSignUpNotification'); |
||||||
|
} |
||||||
|
|
||||||
|
public setContactSignUpNotification(silent: boolean) { |
||||||
|
apiManager.invokeApi('account.setContactSignUpNotification', {silent}) |
||||||
|
.then(value => { |
||||||
|
this.notifyContactsSignUp = Promise.resolve(!silent); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private setFavicon(href: string = 'assets/img/favicon.ico') { |
||||||
|
if(this.prevFavicon === href) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const link = this.faviconEl.cloneNode() as HTMLLinkElement; |
||||||
|
link.href = href; |
||||||
|
this.faviconEl.parentNode.replaceChild(link, this.faviconEl); |
||||||
|
this.faviconEl = link; |
||||||
|
|
||||||
|
this.prevFavicon = href; |
||||||
|
} |
||||||
|
|
||||||
|
public savePeerSettings(key: number | NotifyPeer['_'], settings: PeerNotifySettings) { |
||||||
|
const p = Promise.resolve(settings); |
||||||
|
let obj: any; |
||||||
|
if(typeof(key) === 'number') { |
||||||
|
obj = this.peerSettings['notifyPeer']; |
||||||
|
} |
||||||
|
|
||||||
|
(obj || this.peerSettings)[key] = p; |
||||||
|
|
||||||
|
//rootScope.broadcast('notify_settings', {peerId: peerId});
|
||||||
|
} |
||||||
|
|
||||||
|
public isMuted(peerNotifySettings: PeerNotifySettings) { |
||||||
|
return peerNotifySettings._ === 'peerNotifySettings' && |
||||||
|
(peerNotifySettings.mute_until * 1000) > tsNow(); |
||||||
|
} |
||||||
|
|
||||||
|
public getPeerMuted(peerId: number) { |
||||||
|
return this.getNotifySettings({_: 'inputNotifyPeer', peer: appPeersManager.getInputPeerById(peerId)}) |
||||||
|
.then((peerNotifySettings) => this.isMuted(peerNotifySettings)); |
||||||
|
} |
||||||
|
|
||||||
|
public start() { |
||||||
|
this.updateLocalSettings(); |
||||||
|
//rootScope.on('settings_changed', this.updateNotifySettings);
|
||||||
|
//WebPushApiManager.start();
|
||||||
|
|
||||||
|
if(!this.notificationsUiSupport) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if('Notification' in window && Notification.permission !== 'granted' && Notification.permission !== 'denied') { |
||||||
|
window.addEventListener('click', this.requestPermission); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
if('onbeforeunload' in window) { |
||||||
|
window.addEventListener('beforeunload', this.clear); |
||||||
|
} |
||||||
|
} catch (e) {} |
||||||
|
} |
||||||
|
|
||||||
|
private stop() { |
||||||
|
this.clear(); |
||||||
|
window.clearInterval(this.titleInterval); |
||||||
|
this.titleInterval = 0; |
||||||
|
this.setFavicon(); |
||||||
|
this.stopped = true; |
||||||
|
} |
||||||
|
|
||||||
|
private requestPermission = () => { |
||||||
|
Notification.requestPermission(); |
||||||
|
window.removeEventListener('click', this.requestPermission); |
||||||
|
}; |
||||||
|
|
||||||
|
public notify(data: NotifyOptions) { |
||||||
|
console.log('notify', data, rootScope.idle.isIDLE, this.notificationsUiSupport, this.stopped); |
||||||
|
if(this.stopped) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// FFOS Notification blob src bug workaround
|
||||||
|
/* if(Config.Navigator.ffos && !Config.Navigator.ffos2p) { |
||||||
|
data.image = 'https://telegram.org/img/t_logo.png' |
||||||
|
} |
||||||
|
else if (data.image && !angular.isString(data.image)) { |
||||||
|
if (Config.Navigator.ffos2p) { |
||||||
|
FileManager.getDataUrl(data.image, 'image/jpeg').then(function (url) { |
||||||
|
data.image = url |
||||||
|
notify(data) |
||||||
|
}) |
||||||
|
return false |
||||||
|
} else { |
||||||
|
data.image = FileManager.getUrl(data.image, 'image/jpeg') |
||||||
|
} |
||||||
|
} |
||||||
|
else if (!data.image) */ { |
||||||
|
data.image = 'assets/img/logo.svg'; |
||||||
|
} |
||||||
|
// console.log('notify image', data.image)
|
||||||
|
|
||||||
|
this.notificationsCount++; |
||||||
|
if(!this.titleInterval) { |
||||||
|
this.toggleToggler(); |
||||||
|
} |
||||||
|
|
||||||
|
const now = tsNow(); |
||||||
|
if(this.settings.volume > 0 && !this.settings.nosound/* && |
||||||
|
( |
||||||
|
!data.tag || |
||||||
|
!this.soundsPlayed[data.tag] || |
||||||
|
now > this.soundsPlayed[data.tag] + 60000 |
||||||
|
) */ |
||||||
|
) { |
||||||
|
this.testSound(this.settings.volume); |
||||||
|
this.soundsPlayed[data.tag] = now; |
||||||
|
} |
||||||
|
|
||||||
|
if(!this.notificationsUiSupport || |
||||||
|
'Notification' in window && Notification.permission !== 'granted') { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if(this.settings.nodesktop) { |
||||||
|
if(this.vibrateSupport && !this.settings.novibrate) { |
||||||
|
navigator.vibrate([200, 100, 200]); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const idx = ++this.notificationIndex; |
||||||
|
const key = data.key || 'k' + idx; |
||||||
|
let notification: MyNotification; |
||||||
|
|
||||||
|
if('Notification' in window) { |
||||||
|
try { |
||||||
|
if(data.tag) { |
||||||
|
for(let i in this.notificationsShown) { |
||||||
|
const notification = this.notificationsShown[i]; |
||||||
|
if(notification && |
||||||
|
notification.tag === data.tag) { |
||||||
|
notification.hidden = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
notification = new Notification(data.title, { |
||||||
|
icon: data.image || '', |
||||||
|
body: data.message || '', |
||||||
|
tag: data.tag || '', |
||||||
|
silent: data.silent || false |
||||||
|
}); |
||||||
|
} catch(e) { |
||||||
|
this.notificationsUiSupport = false; |
||||||
|
//WebPushApiManager.setLocalNotificationsDisabled();
|
||||||
|
return |
||||||
|
} |
||||||
|
} /* else if('mozNotification' in navigator) { |
||||||
|
notification = navigator.mozNotification.createNotification(data.title, data.message || '', data.image || '') |
||||||
|
} else if(notificationsMsSiteMode) { |
||||||
|
window.external.msSiteModeClearIconOverlay() |
||||||
|
window.external.msSiteModeSetIconOverlay('img/icons/icon16.png', data.title) |
||||||
|
window.external.msSiteModeActivate() |
||||||
|
notification = { |
||||||
|
index: idx |
||||||
|
} |
||||||
|
} */ else { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
notification.onclick = () => { |
||||||
|
notification.close(); |
||||||
|
//AppRuntimeManager.focus();
|
||||||
|
this.clear(); |
||||||
|
if(data.onclick) { |
||||||
|
data.onclick(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
notification.onclose = () => { |
||||||
|
if(!notification.hidden) { |
||||||
|
delete this.notificationsShown[key]; |
||||||
|
this.clear(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if(notification.show) { |
||||||
|
notification.show(); |
||||||
|
} |
||||||
|
this.notificationsShown[key] = notification; |
||||||
|
|
||||||
|
if(!isMobile) { |
||||||
|
setTimeout(() => { |
||||||
|
this.hide(key); |
||||||
|
}, 8000); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public testSound(volume: number) { |
||||||
|
const now = tsNow(); |
||||||
|
if(this.nextSoundAt && now < this.nextSoundAt && this.prevSoundVolume === volume) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.nextSoundAt = now + 1000; |
||||||
|
this.prevSoundVolume = volume; |
||||||
|
const filename = 'assets/audio/notification.mp3'; |
||||||
|
const audio = document.createElement('audio'); |
||||||
|
audio.autoplay = true; |
||||||
|
audio.setAttribute('mozaudiochannel', 'notification'); |
||||||
|
audio.volume = volume; |
||||||
|
audio.innerHTML = ` |
||||||
|
<source src="${filename}" type="audio/mpeg" /> |
||||||
|
<embed hidden="true" autostart="true" loop="false" volume="${volume * 100}" src="${filename}" /> |
||||||
|
`;
|
||||||
|
this.notifySoundEl.append(audio); |
||||||
|
|
||||||
|
audio.addEventListener('ended', () => { |
||||||
|
audio.remove(); |
||||||
|
}, {once: true}); |
||||||
|
} |
||||||
|
|
||||||
|
public cancel(key: string) { |
||||||
|
const notification = this.notificationsShown[key]; |
||||||
|
if(notification) { |
||||||
|
if(this.notificationsCount > 0) { |
||||||
|
this.notificationsCount--; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
if(notification.close) { |
||||||
|
notification.hidden = true; |
||||||
|
notification.close(); |
||||||
|
}/* else if(notificationsMsSiteMode && |
||||||
|
notification.index === notificationIndex) { |
||||||
|
window.external.msSiteModeClearIconOverlay() |
||||||
|
} */ |
||||||
|
} catch (e) {} |
||||||
|
|
||||||
|
delete this.notificationsShown[key]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private hide(key: string) { |
||||||
|
const notification = this.notificationsShown[key]; |
||||||
|
if(notification) { |
||||||
|
try { |
||||||
|
if(notification.close) { |
||||||
|
notification.hidden = true; |
||||||
|
notification.close(); |
||||||
|
} |
||||||
|
} catch (e) {} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public soundReset(tag: string) { |
||||||
|
delete this.soundsPlayed[tag]; |
||||||
|
} |
||||||
|
|
||||||
|
public clear() { |
||||||
|
/* if(notificationsMsSiteMode) { |
||||||
|
window.external.msSiteModeClearIconOverlay() |
||||||
|
} else { */ |
||||||
|
for(let i in this.notificationsShown) { |
||||||
|
const notification = this.notificationsShown[i]; |
||||||
|
try { |
||||||
|
if(notification.close) { |
||||||
|
notification.close(); |
||||||
|
} |
||||||
|
} catch (e) {} |
||||||
|
} |
||||||
|
/* } */ |
||||||
|
this.notificationsShown = {}; |
||||||
|
this.notificationsCount = 0; |
||||||
|
|
||||||
|
//WebPushApiManager.hidePushNotifications();
|
||||||
|
} |
||||||
|
|
||||||
|
private registerDevice(tokenData: any) { |
||||||
|
if(this.registeredDevice && |
||||||
|
deepEqual(this.registeredDevice, tokenData)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
apiManager.invokeApi('account.registerDevice', { |
||||||
|
token_type: tokenData.tokenType, |
||||||
|
token: tokenData.tokenValue, |
||||||
|
other_uids: [], |
||||||
|
app_sandbox: false, |
||||||
|
secret: new Uint8Array() |
||||||
|
}).then(() => { |
||||||
|
this.registeredDevice = tokenData; |
||||||
|
}, (error) => { |
||||||
|
error.handled = true; |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
private unregisterDevice(tokenData: any) { |
||||||
|
if(!this.registeredDevice) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
apiManager.invokeApi('account.unregisterDevice', { |
||||||
|
token_type: tokenData.tokenType, |
||||||
|
token: tokenData.tokenValue, |
||||||
|
other_uids: [] |
||||||
|
}).then(() => { |
||||||
|
this.registeredDevice = false |
||||||
|
}, (error) => { |
||||||
|
error.handled = true |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
public getVibrateSupport() { |
||||||
|
return this.vibrateSupport |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const appNotificationsManager = new AppNotificationsManager(); |
||||||
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appNotificationsManager = appNotificationsManager); |
||||||
|
export default appNotificationsManager; |
Loading…
Reference in new issue