Telegram Web K with changes to work inside I2P
https://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.
316 lines
11 KiB
316 lines
11 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap, Config, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, PhoneCall, MethodDeclMap, MessageReactions, ReactionCount } from "../layer"; |
|
import type { MyDocument } from "./appManagers/appDocsManager"; |
|
import type { AppMessagesManager, Dialog, MessagesStorage, MyMessage } from "./appManagers/appMessagesManager"; |
|
import type { MyDialogFilter } from "./storages/filters"; |
|
import type { Folder } from "./storages/dialogs"; |
|
import type { UserTyping } from "./appManagers/appProfileManager"; |
|
import type { State, Theme } from "./appManagers/appStateManager"; |
|
import type { MyDraftMessage } from "./appManagers/appDraftsManager"; |
|
import type { PushSubscriptionNotify } from "./mtproto/webPushApiManager"; |
|
import type { PushNotificationObject } from "./serviceWorker/push"; |
|
import type { ConnectionStatusChange } from "./mtproto/connectionStatus"; |
|
import type { GroupCallId } from "./appManagers/appGroupCallsManager"; |
|
import type { AppMediaPlaybackController } from "../components/appMediaPlaybackController"; |
|
import type GroupCallInstance from "./calls/groupCallInstance"; |
|
import type CallInstance from "./calls/callInstance"; |
|
import type { StreamAmplitude } from "./calls/streamManager"; |
|
import type Chat from "../components/chat/chat"; |
|
import { NULL_PEER_ID, UserAuth } from "./mtproto/mtproto_config"; |
|
import EventListenerBase from "../helpers/eventListenerBase"; |
|
import { MOUNT_CLASS_TO } from "../config/debug"; |
|
import { MTAppConfig } from "./mtproto/appConfig"; |
|
|
|
export type BroadcastEvents = { |
|
'chat_full_update': ChatId, |
|
'chat_update': ChatId, |
|
|
|
'channel_update': ChatId, |
|
|
|
'user_update': UserId, |
|
'user_auth': UserAuth, |
|
'user_full_update': UserId, |
|
|
|
'chat_changing': {from: Chat, to: Chat}, |
|
|
|
'peer_changed': PeerId, |
|
'peer_changing': Chat, |
|
'peer_pinned_messages': {peerId: PeerId, mids?: number[], pinned?: boolean, unpinAll?: true}, |
|
'peer_pinned_hidden': {peerId: PeerId, maxId: number}, |
|
'peer_typings': {peerId: PeerId, typings: UserTyping[]}, |
|
'peer_block': {peerId: PeerId, blocked: boolean}, |
|
'peer_title_edit': PeerId, |
|
'peer_bio_edit': PeerId, |
|
'peer_deleted': PeerId, // left chat, deleted user dialog, left channel |
|
'peer_full_update': PeerId, |
|
|
|
'filter_delete': MyDialogFilter, |
|
'filter_update': MyDialogFilter, |
|
'filter_new': MyDialogFilter, |
|
'filter_order': number[], |
|
|
|
'folder_unread': Folder, |
|
|
|
'dialog_draft': {peerId: PeerId, dialog: Dialog, drop: boolean, draft: MyDraftMessage | undefined, index: number}, |
|
'dialog_unread': {peerId: PeerId}, |
|
'dialog_flush': {peerId: PeerId}, |
|
'dialog_drop': {peerId: PeerId, dialog?: Dialog}, |
|
'dialog_migrate': {migrateFrom: PeerId, migrateTo: PeerId}, |
|
//'dialog_top': Dialog, |
|
'dialog_notify_settings': Dialog, |
|
// 'dialog_order': {dialog: Dialog, pos: number}, |
|
'dialogs_multiupdate': {[peerId: PeerId]: Dialog}, |
|
|
|
'history_append': {storage: MessagesStorage, peerId: PeerId, mid: number}, |
|
'history_update': {storage: MessagesStorage, peerId: PeerId, mid: number}, |
|
'history_reply_markup': {peerId: PeerId}, |
|
'history_multiappend': AppMessagesManager['newMessagesToHandle'], |
|
'history_delete': {peerId: PeerId, msgs: Set<number>}, |
|
'history_forbidden': PeerId, |
|
'history_reload': PeerId, |
|
'history_focus': {peerId: PeerId, threadId?: number, mid?: number, startParam?: string}, |
|
//'history_request': void, |
|
|
|
'message_edit': {storage: MessagesStorage, peerId: PeerId, mid: number}, |
|
'message_sent': {storage: MessagesStorage, tempId: number, tempMessage: any, mid: number, message: MyMessage}, |
|
'messages_views': {peerId: PeerId, mid: number, views: number}[], |
|
'messages_reactions': {message: Message.message, changedResults: ReactionCount[]}[], |
|
'messages_pending': void, |
|
'messages_read': void, |
|
'messages_downloaded': {peerId: PeerId, mids: number[]}, |
|
'messages_media_read': {peerId: PeerId, mids: number[]}, |
|
|
|
'replies_updated': Message.message, |
|
|
|
'scheduled_new': {peerId: PeerId, mid: number}, |
|
'scheduled_delete': {peerId: PeerId, mids: number[]}, |
|
|
|
'album_edit': {peerId: PeerId, groupId: string, deletedMids: number[]}, |
|
|
|
'stickers_installed': StickerSet.stickerSet, |
|
'stickers_deleted': StickerSet.stickerSet, |
|
|
|
'media_play': ReturnType<AppMediaPlaybackController['getPlayingDetails']>, |
|
'media_pause': void, |
|
'media_playback_params': ReturnType<AppMediaPlaybackController['getPlaybackParams']>, |
|
'media_stop': void, |
|
|
|
'state_cleared': void, |
|
'state_synchronized': ChatId | void, |
|
'state_synchronizing': ChatId | void, |
|
|
|
'contacts_update': UserId, |
|
'avatar_update': PeerId, |
|
'poll_update': {poll: Poll, results: PollResults}, |
|
'invalidate_participants': ChatId, |
|
//'channel_settings': {channelId: number}, |
|
'webpage_updated': {id: WebPage.webPage['id'], msgs: {peerId: PeerId, mid: number, isScheduled: boolean}[]}, |
|
|
|
'connection_status_change': ConnectionStatusChange, |
|
'settings_updated': {key: string, value: any}, |
|
'draft_updated': {peerId: PeerId, threadId: number, draft: MyDraftMessage | undefined, force?: boolean}, |
|
|
|
'event-heavy-animation-start': void, |
|
'event-heavy-animation-end': void, |
|
|
|
'im_tab_change': number, |
|
|
|
'idle': boolean, |
|
|
|
'overlay_toggle': boolean, |
|
|
|
'background_change': void, |
|
|
|
'privacy_update': Update.updatePrivacy, |
|
|
|
'notify_settings': Update.updateNotifySettings, |
|
'notify_peer_type_settings': {key: Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings}, |
|
|
|
'language_change': string, |
|
|
|
'theme_change': void, |
|
|
|
'instance_activated': void, |
|
'instance_deactivated': void, |
|
|
|
'push_notification_click': PushNotificationObject, |
|
'push_init': PushSubscriptionNotify, |
|
'push_subscribe': PushSubscriptionNotify, |
|
'push_unsubscribe': PushSubscriptionNotify, |
|
|
|
'emoji_recent': string, |
|
|
|
'download_start': DocId, |
|
'download_progress': any, |
|
'document_downloaded': MyDocument, |
|
|
|
'context_menu_toggle': boolean, |
|
'choosing_sticker': boolean |
|
|
|
'group_call_instance': GroupCallInstance, |
|
'group_call_update': GroupCall, |
|
'group_call_amplitude': {amplitudes: StreamAmplitude[], type: 'all' | 'input'}, |
|
'group_call_participant': {groupCallId: GroupCallId, participant: GroupCallParticipant}, |
|
// 'group_call_video_track_added': {instance: GroupCallInstance} |
|
|
|
'call_instance': {hasCurrent: boolean, instance: CallInstance}, |
|
'call_accepting': CallInstance, // это костыль. используется при параллельном вызове, чтобы заменить звонок в topbarCall |
|
'call_incompatible': UserId, |
|
|
|
'quick_reaction': string, |
|
|
|
'missed_reactions_element': {message: Message.message, changedResults: ReactionCount[]}, |
|
|
|
'service_notification': Update.updateServiceNotification |
|
}; |
|
|
|
export class RootScope extends EventListenerBase<{ |
|
[name in Update['_']]: (update: ConstructorDeclMap[name]) => void |
|
} & { |
|
[name in keyof BroadcastEvents]: (e: BroadcastEvents[name]) => void |
|
}> { |
|
public overlaysActive = 0; |
|
public myId: PeerId; |
|
public idle = { |
|
isIDLE: true, |
|
deactivated: false, |
|
focusPromise: Promise.resolve(), |
|
focusResolve: () => {} |
|
}; |
|
public connectionStatus: {[name: string]: ConnectionStatusChange} = {}; |
|
public settings: State['settings']; |
|
public peerId: PeerId; |
|
public filterId = 0; |
|
public systemTheme: Theme['name']; |
|
public config: Partial<Config.config> = { |
|
forwarded_count_max: 100, |
|
edit_time_limit: 86400 * 2, |
|
pinned_dialogs_count_max: 5, |
|
pinned_infolder_count_max: 100, |
|
message_length_max: 4096, |
|
caption_length_max: 1024, |
|
}; |
|
public appConfig: MTAppConfig; |
|
|
|
public themeColor: string; |
|
private _themeColorElem: Element; |
|
|
|
constructor() { |
|
super(); |
|
|
|
this.addEventListener('peer_changed', (peerId) => { |
|
this.peerId = peerId; |
|
document.body.classList.toggle('has-chat', !!peerId); |
|
}); |
|
|
|
this.addEventListener('user_auth', ({id}) => { |
|
// @ts-ignore |
|
this.myId = typeof(NULL_PEER_ID) === 'number' ? +id : '' + id; |
|
}); |
|
|
|
this.addEventListener('connection_status_change', (status) => { |
|
this.connectionStatus[status.name] = status; |
|
}); |
|
|
|
this.addEventListener('idle', (isIDLE) => { |
|
if(isIDLE) { |
|
this.idle.focusPromise = new Promise((resolve) => { |
|
this.idle.focusResolve = resolve; |
|
}); |
|
} else { |
|
this.idle.focusResolve(); |
|
} |
|
}); |
|
} |
|
|
|
get themeColorElem() { |
|
if(this._themeColorElem !== undefined) { |
|
return this._themeColorElem; |
|
} |
|
|
|
return this._themeColorElem = document.head.querySelector('[name="theme-color"]') as Element || null; |
|
} |
|
|
|
public setThemeColor(color = this.themeColor) { |
|
if(!color) { |
|
color = this.isNight() ? '#212121' : '#ffffff'; |
|
} |
|
|
|
const themeColorElem = this.themeColorElem; |
|
if(themeColorElem) { |
|
themeColorElem.setAttribute('content', color); |
|
} |
|
} |
|
|
|
public setThemeListener() { |
|
try { |
|
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); |
|
const checkDarkMode = () => { |
|
//const theme = this.getTheme(); |
|
this.systemTheme = darkModeMediaQuery.matches ? 'night' : 'day'; |
|
//const newTheme = this.getTheme(); |
|
|
|
if(this.myId) { |
|
this.dispatchEvent('theme_change'); |
|
} else { |
|
this.setTheme(); |
|
} |
|
}; |
|
|
|
if('addEventListener' in darkModeMediaQuery) { |
|
darkModeMediaQuery.addEventListener('change', checkDarkMode); |
|
} else if('addListener' in darkModeMediaQuery) { |
|
(darkModeMediaQuery as any).addListener(checkDarkMode); |
|
} |
|
|
|
checkDarkMode(); |
|
} catch(err) { |
|
|
|
} |
|
} |
|
|
|
public setTheme() { |
|
const isNight = this.isNight(); |
|
const colorScheme = document.head.querySelector('[name="color-scheme"]'); |
|
if(colorScheme) { |
|
colorScheme.setAttribute('content', isNight ? 'dark' : 'light'); |
|
} |
|
|
|
document.documentElement.classList.toggle('night', isNight); |
|
this.setThemeColor(); |
|
} |
|
|
|
get isOverlayActive() { |
|
return this.overlaysActive > 0; |
|
} |
|
|
|
set isOverlayActive(value: boolean) { |
|
this.overlaysActive += value ? 1 : -1; |
|
this.dispatchEvent('overlay_toggle', this.isOverlayActive); |
|
} |
|
|
|
public isNight() { |
|
return this.getTheme().name === 'night'; |
|
} |
|
|
|
public getTheme(name: Theme['name'] = this.settings.theme === 'system' ? this.systemTheme : this.settings.theme) { |
|
return this.settings.themes.find(t => t.name === name); |
|
} |
|
} |
|
|
|
const rootScope = new RootScope(); |
|
MOUNT_CLASS_TO.rootScope = rootScope; |
|
export default rootScope; |
|
|
|
/* rootScope.addEventListener('album_edit', (e) => { |
|
|
|
}); |
|
|
|
rootScope.addEventListener<'album_edit'>('album_edit', (e) => { |
|
|
|
}); */
|
|
|