Eduard Kuzmenko
4 years ago
46 changed files with 1770 additions and 427 deletions
@ -1,78 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -1,48 +1,50 @@
|
||||
import appStateManager from "../lib/appManagers/appStateManager"; |
||||
import { getDeepProperty } from "../helpers/object"; |
||||
|
||||
const RadioField = (text: string, name: string, value?: string, stateKey?: string) => { |
||||
const label = document.createElement('label'); |
||||
label.classList.add('radio-field'); |
||||
|
||||
const input = document.createElement('input'); |
||||
input.type = 'radio'; |
||||
/* input.id = */input.name = 'input-radio-' + name; |
||||
|
||||
if(value) { |
||||
input.value = value; |
||||
|
||||
if(stateKey) { |
||||
appStateManager.getState().then(state => { |
||||
input.checked = getDeepProperty(state, stateKey) === value; |
||||
}); |
||||
|
||||
input.addEventListener('change', () => { |
||||
appStateManager.setByKey(stateKey, value); |
||||
}); |
||||
export default class RadioField { |
||||
public input: HTMLInputElement; |
||||
public label: HTMLLabelElement; |
||||
public main: HTMLElement; |
||||
|
||||
constructor(text: string, name: string, value?: string, stateKey?: string) { |
||||
const label = this.label = document.createElement('label'); |
||||
label.classList.add('radio-field'); |
||||
|
||||
const input = this.input = document.createElement('input'); |
||||
input.type = 'radio'; |
||||
/* input.id = */input.name = 'input-radio-' + name; |
||||
|
||||
if(value) { |
||||
input.value = value; |
||||
|
||||
if(stateKey) { |
||||
appStateManager.getState().then(state => { |
||||
input.checked = getDeepProperty(state, stateKey) === value; |
||||
}); |
||||
|
||||
input.addEventListener('change', () => { |
||||
appStateManager.setByKey(stateKey, value); |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
const main = document.createElement('div'); |
||||
main.classList.add('radio-field-main'); |
||||
|
||||
if(text) { |
||||
main.innerHTML = text; |
||||
/* const caption = document.createElement('div'); |
||||
caption.classList.add('radio-field-main-caption'); |
||||
caption.innerHTML = text; |
||||
|
||||
if(subtitle) { |
||||
label.classList.add('radio-field-with-subtitle'); |
||||
caption.insertAdjacentHTML('beforeend', `<div class="radio-field-main-subtitle">${subtitle}</div>`); |
||||
|
||||
const main = this.main = document.createElement('div'); |
||||
main.classList.add('radio-field-main'); |
||||
|
||||
if(text) { |
||||
main.innerHTML = text; |
||||
/* const caption = document.createElement('div'); |
||||
caption.classList.add('radio-field-main-caption'); |
||||
caption.innerHTML = text; |
||||
|
||||
if(subtitle) { |
||||
label.classList.add('radio-field-with-subtitle'); |
||||
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 @@
@@ -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 @@
@@ -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