Telegram Web, preconfigured for usage in I2P. http://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
12 KiB

console.log('[SW] Push worker started')
var pendingNotification = false
var defaultBaseUrl
switch (location.hostname) {
case 'localhost':
defaultBaseUrl = 'http://localhost:8000/app/index.html#/im'
break
case 'zhukov.github.io':
defaultBaseUrl = 'https://zhukov.github.io/webogram/#/im'
break
default:
case 'web.telegram.org':
defaultBaseUrl = 'https://' + location.hostname + '/#/im'
}
self.addEventListener('push', function(event) {
var obj = event.data.json()
console.log('[SW] push', obj)
var hasActiveWindows = false
var checksPromise = new Promise(function (resolve, reject) {
var nowTime = +(new Date())
Promise.all([getMuteUntil(), getLastAliveTime()]).then(function (result) {
var muteUntil = result[0]
var lastAliveTime = result[1]
return clients.matchAll({type: 'window'}).then(function(clientList) {
console.log('matched clients', clientList)
hasActiveWindows = clientList.length > 0
if (hasActiveWindows) {
console.log('Supress notification because some instance is alive')
return reject()
}
if (userInvisibleIsSupported() &&
muteUntil &&
nowTime < muteUntil) {
console.log('Supress notification because mute for ', Math.ceil((muteUntil - nowTime) / 60000), 'min')
return reject()
}
if (!obj.badge) {
return reject()
}
return resolve()
})
})
})
var notificationPromise = checksPromise.then(function () {
return Promise.all([getSettings(), getLang()]).then(function (result) {
return fireNotification(obj, result[0], result[1])
})
})
var closePromise = notificationPromise.catch(function () {
console.log('[SW] Closing all notifications on push', hasActiveWindows)
if (userInvisibleIsSupported() || hasActiveWindows) {
return closeAllNotifications()
}
return self.registration.showNotification('Telegram', {
tag: 'unknown_peer'
}).then(function () {
if (hasActiveWindows) {
return closeAllNotifications()
}
setTimeout(closeAllNotifications, hasActiveWindows ? 0 : 100)
}).catch(function (error) {
console.error('Show notification error', error)
})
})
event.waitUntil(closePromise)
})
self.addEventListener('install', function(event) {
event.waitUntil(self.skipWaiting());
});
self.addEventListener('activate', function(event) {
console.log('[SW] on activate')
event.waitUntil(self.clients.claim());
});
self.addEventListener('message', function(event) {
console.log('[SW] on message', event.data)
var client = event.ports && event.ports[0] || event.source
if (event.data.type == 'ping') {
if (event.data.localNotifications) {
lastAliveTime = +(new Date())
IDBManager.setItem('push_last_alive', lastAliveTime)
}
if (pendingNotification &&
client &&
'postMessage' in client) {
client.postMessage(pendingNotification)
pendingNotification = false
}
if (event.data.lang) {
lang = event.data.lang
IDBManager.setItem('push_lang', lang)
}
if (event.data.settings) {
settings = event.data.settings
IDBManager.setItem('push_settings', settings)
}
}
if (event.data.type == 'notifications_clear') {
closeAllNotifications()
}
})
function fireNotification(obj, settings, lang) {
var title = obj.title || 'Telegram'
var body = obj.description || ''
var icon = 'img/logo_share.png'
var peerID
if (obj.custom && obj.custom.channel_id) {
peerID = -obj.custom.channel_id
}
else if (obj.custom && obj.custom.chat_id) {
peerID = -obj.custom.chat_id
}
else {
peerID = obj.custom && obj.custom.from_id || 0
}
obj.custom.peerID = peerID
var tag = 'peer' + peerID
if (settings && settings.nopreview) {
title = 'Telegram'
body = lang.push_message_nopreview || 'You have a new message'
tag = 'unknown_peer'
}
console.log('[SW] show notify', title, body, icon, obj)
var notificationPromise = self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: obj,
actions: [
{
action: 'mute1d',
title: lang.push_action_mute1d || 'Mute for 24H'
},
{
action: 'push_settings',
title: lang.push_action_settings || 'Settings'
}
]
})
return notificationPromise.then(function (event) {
if (event && event.notification) {
pushToNotifications(event.notification)
}
return Promise.resolve()
}).catch(function (error) {
console.error('Show notification promise', error)
})
}
var notifications = []
function pushToNotifications(notification) {
if (notifications.indexOf(notification) == -1) {
notifications.push(notification)
notification.onclose = onCloseNotification
}
}
function onCloseNotification(event) {
removeFromNotifications(event.notification)
}
function removeFromNotifications(notification) {
var pos = notifications.indexOf(notification)
if (pos != -1) {
notifications.splice(pos, 1)
}
}
function closeAllNotifications() {
for (var i = 0, len = notifications.length; i < len; i++) {
try {
notifications[i].close()
} catch (e) {}
}
var promise
if ('getNotifications' in self.registration) {
promise = self.registration.getNotifications({}).then(function(notifications) {
for (var i = 0, len = notifications.length; i < len; i++) {
try {
notifications[i].close()
} catch (e) {}
}
}).catch(function (error) {
console.error('Offline register SW error', error)
})
} else {
promise = Promise.resolve()
}
notifications = []
return promise
}
self.addEventListener('notificationclick', function(event) {
var notification = event.notification
console.log('On notification click: ', notification.tag)
notification.close()
var action = event.action
if (action == 'mute1d' && userInvisibleIsSupported()) {
console.log('[SW] mute for 1d')
muteUntil = +(new Date()) + 86400000
IDBManager.setItem('push_mute_until', muteUntil)
return
}
if (!notification.data) {
return
}
var promise = clients.matchAll({
type: 'window'
}).then(function(clientList) {
notification.data.action = action
pendingNotification = {type: 'push_click', data: notification.data}
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i]
if ('focus' in client) {
client.focus()
client.postMessage(pendingNotification)
pendingNotification = false
return
}
}
if (clients.openWindow) {
return getSettings().then(function (settings) {
return clients.openWindow(settings.baseUrl || defaultBaseUrl)
})
}
}).catch(function (error) {
console.error('Clients.matchAll error', error)
})
event.waitUntil(promise)
})
self.addEventListener('notificationclose', onCloseNotification)
;(function () {
var dbName = 'keyvalue'
var dbStoreName = 'kvItems'
var dbVersion = 2
var openDbPromise
var idbIsAvailable = self.indexedDB !== undefined &&
self.IDBTransaction !== undefined
function isAvailable () {
return idbIsAvailable
}
function openDatabase () {
if (openDbPromise) {
return openDbPromise
}
return openDbPromise = new Promise(function (resolve, reject) {
try {
var request = indexedDB.open(dbName, dbVersion)
var createObjectStore = function (db) {
db.createObjectStore(dbStoreName)
}
if (!request) {
return reject()
}
} catch (error) {
console.error('error opening db', error.message)
idbIsAvailable = false
return reject(error)
}
var finished = false
setTimeout(function () {
if (!finished) {
request.onerror({type: 'IDB_CREATE_TIMEOUT'})
}
}, 3000)
request.onsuccess = function (event) {
finished = true
var db = request.result
db.onerror = function (error) {
idbIsAvailable = false
console.error('Error creating/accessing IndexedDB database', error)
reject(error)
}
resolve(db)
}
request.onerror = function (event) {
finished = true
idbIsAvailable = false
console.error('Error creating/accessing IndexedDB database', event)
reject(event)
}
request.onupgradeneeded = function (event) {
finished = true
console.warn('performing idb upgrade from', event.oldVersion, 'to', event.newVersion)
var db = event.target.result
if (event.oldVersion == 1) {
db.deleteObjectStore(dbStoreName)
}
createObjectStore(db)
}
})
}
function setItem (key, value) {
return openDatabase().then(function (db) {
try {
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName)
var request = objectStore.put(value, key)
} catch (error) {
idbIsAvailable = false
return Promise.reject(error)
}
return new Promise(function(resolve, reject) {
request.onsuccess = function (event) {
resolve(value)
}
request.onerror = function (error) {
reject(error)
}
})
})
}
function getItem (key) {
return openDatabase().then(function (db) {
return new Promise(function(resolve, reject) {
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ || 'readonly').objectStore(dbStoreName)
var request = objectStore.get(key)
request.onsuccess = function (event) {
var result = event.target.result
if (result === undefined) {
resolve()
} else {
resolve(result)
}
}
request.onerror = function (error) {
reject(error)
}
})
})
}
openDatabase()
self.IDBManager = {
name: 'IndexedDB',
isAvailable: isAvailable,
setItem: setItem,
getItem: getItem
}
})()
var lastAliveTime, muteUntil, settings, lang
function getMuteUntil() {
if (muteUntil !== undefined) {
return Promise.resolve(muteUntil)
}
return IDBManager.getItem('push_mute_until').then(function (newMuteUntil) {
return muteUntil = Math.max(muteUntil || 0, newMuteUntil || 0) || false
}).catch(function (error) {
console.error('IDB error', error)
return false
})
}
function getLastAliveTime() {
if (lastAliveTime !== undefined) {
return Promise.resolve(lastAliveTime)
}
return IDBManager.getItem('push_last_alive').then(function (newLastAliveTime) {
return lastAliveTime = Math.max(lastAliveTime || 0, newLastAliveTime || 0) || false
}).catch(function (error) {
console.error('IDB error', error)
return false
})
}
function getLang() {
if (lang !== undefined) {
return Promise.resolve(lang)
}
return IDBManager.getItem('push_lang').then(function (newLang) {
return lang = newLang || {}
}).catch(function (error) {
console.error('IDB error', error)
return {}
})
}
function getSettings() {
if (settings !== undefined) {
return Promise.resolve(settings)
}
return IDBManager.getItem('push_settings').then(function (newSettings) {
return settings = newSettings || {}
}).catch(function (error) {
console.error('IDB error', error)
return {}
})
}
function userInvisibleIsSupported() {
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
return isFirefox ? true : false
}