Merge branch 'calls'
This commit is contained in:
commit
5f78b9e4cb
310
package-lock.json
generated
310
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,8 @@
|
||||
"author": "",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -35,6 +37,7 @@
|
||||
"@cryptography/sha1": "^0.2.0",
|
||||
"@cryptography/sha256": "^0.2.0",
|
||||
"@peculiar/webcrypto": "^1.1.7",
|
||||
"@types/big-integer": "^0.0.31",
|
||||
"@types/chrome": "0.0.139",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/serviceworker-webpack-plugin": "^1.0.2",
|
||||
@ -42,6 +45,7 @@
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"big-integer": "^1.6.51",
|
||||
"compression": "^1.7.4",
|
||||
"css-loader": "^3.6.0",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
|
@ -8,8 +8,9 @@ import rootScope from "../lib/rootScope";
|
||||
import { IS_SAFARI } from "../environment/userAgent";
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import isInDOM from "../helpers/dom/isInDOM";
|
||||
import { forEachReverse, indexOfAndSplice } from "../helpers/array";
|
||||
import RLottiePlayer from "../lib/rlottie/rlottiePlayer";
|
||||
import indexOfAndSplice from "../helpers/array/indexOfAndSplice";
|
||||
import forEachReverse from "../helpers/array/forEachReverse";
|
||||
|
||||
export interface AnimationItem {
|
||||
el: HTMLElement,
|
||||
|
@ -13,8 +13,7 @@ import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import appDownloadManager from "../lib/appManagers/appDownloadManager";
|
||||
import simulateEvent from "../helpers/dom/dispatchEvent";
|
||||
import type { SearchSuperContext } from "./appSearchSuper.";
|
||||
import { copy, deepEqual } from "../helpers/object";
|
||||
import { DocumentAttribute, Message, MessageMedia, PhotoSize } from "../layer";
|
||||
import { DocumentAttribute, Message, PhotoSize } from "../layer";
|
||||
import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
|
||||
import appAvatarsManager from "../lib/appManagers/appAvatarsManager";
|
||||
@ -22,6 +21,8 @@ import appPeersManager from "../lib/appManagers/appPeersManager";
|
||||
import I18n from "../lib/langPack";
|
||||
import SearchListLoader from "../helpers/searchListLoader";
|
||||
import { onMediaLoad } from "../helpers/files";
|
||||
import copy from "../helpers/object/copy";
|
||||
import deepEqual from "../helpers/object/deepEqual";
|
||||
|
||||
// TODO: Safari: проверить стрим, включить его и сразу попробовать включить видео или другую песню
|
||||
// TODO: Safari: попробовать замаскировать подгрузку последнего чанка
|
||||
|
@ -7,11 +7,10 @@
|
||||
import { MOUNT_CLASS_TO } from "../config/debug";
|
||||
import { IS_MOBILE_SAFARI } from "../environment/userAgent";
|
||||
import { logger } from "../lib/logger";
|
||||
import { doubleRaf } from "../helpers/schedulers";
|
||||
import blurActiveElement from "../helpers/dom/blurActiveElement";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import { indexOfAndSplice } from "../helpers/array";
|
||||
import isSwipingBackSafari from "../helpers/dom/isSwipingBackSafari";
|
||||
import indexOfAndSplice from "../helpers/array/indexOfAndSplice";
|
||||
|
||||
export type NavigationItem = {
|
||||
type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' |
|
||||
|
@ -4,7 +4,6 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { copy, getObjectKeysAndSort, safeAssign } from "../helpers/object";
|
||||
import { escapeRegExp, limitSymbols } from "../helpers/string";
|
||||
import appChatsManager from "../lib/appManagers/appChatsManager";
|
||||
import appDialogsManager from "../lib/appManagers/appDialogsManager";
|
||||
@ -51,6 +50,9 @@ import { attachClickEvent, simulateClickEvent } from "../helpers/dom/clickEvent"
|
||||
import { MyDocument } from "../lib/appManagers/appDocsManager";
|
||||
import AppMediaViewer from "./appMediaViewer";
|
||||
import lockTouchScroll from "../helpers/dom/lockTouchScroll";
|
||||
import copy from "../helpers/object/copy";
|
||||
import getObjectKeysAndSort from "../helpers/object/getObjectKeysAndSort";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
//const testScroll = false;
|
||||
|
||||
|
@ -13,19 +13,20 @@ import Scrollable from "./scrollable";
|
||||
import { FocusDirection } from "../helpers/fastSmoothScroll";
|
||||
import CheckboxField from "./checkboxField";
|
||||
import appProfileManager from "../lib/appManagers/appProfileManager";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
|
||||
import findUpAttribute from "../helpers/dom/findUpAttribute";
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import PeerTitle from "./peerTitle";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import replaceContent from "../helpers/dom/replaceContent";
|
||||
import { filterUnique, indexOfAndSplice } from "../helpers/array";
|
||||
import debounce from "../helpers/schedulers/debounce";
|
||||
import windowSize from "../helpers/windowSize";
|
||||
import appPeersManager, { IsPeerType } from "../lib/appManagers/appPeersManager";
|
||||
import { generateDelimiter, SettingSection } from "./sidebarLeft";
|
||||
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
||||
import filterUnique from "../helpers/array/filterUnique";
|
||||
import indexOfAndSplice from "../helpers/array/indexOfAndSplice";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
type SelectSearchPeerType = 'contacts' | 'dialogs' | 'channelParticipants';
|
||||
|
||||
|
@ -17,7 +17,7 @@ import appAvatarsManager from "../lib/appManagers/appAvatarsManager";
|
||||
import AppMediaViewer from "./appMediaViewer";
|
||||
import AppMediaViewerAvatar from "./appMediaViewerAvatar";
|
||||
import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config";
|
||||
import { isObject } from "../helpers/object";
|
||||
import isObject from "../helpers/object/isObject";
|
||||
|
||||
const onAvatarUpdate = (peerId: PeerId) => {
|
||||
appAvatarsManager.removeFromAvatarsCache(peerId);
|
||||
|
424
src/components/call/index.ts
Normal file
424
src/components/call/index.ts
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import IS_SCREEN_SHARING_SUPPORTED from "../../environment/screenSharingSupport";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import ControlsHover from "../../helpers/dom/controlsHover";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import { addFullScreenListener, cancelFullScreen, isFullScreen, requestFullScreen } from "../../helpers/dom/fullScreen";
|
||||
import { MediaSize } from "../../helpers/mediaSizes";
|
||||
import MovablePanel from "../../helpers/movablePanel";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
import toggleClassName from "../../helpers/toggleClassName";
|
||||
import type { AppAvatarsManager } from "../../lib/appManagers/appAvatarsManager";
|
||||
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
|
||||
import CallInstance from "../../lib/calls/callInstance";
|
||||
import CALL_STATE from "../../lib/calls/callState";
|
||||
import I18n, { i18n } from "../../lib/langPack";
|
||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import animationIntersector from "../animationIntersector";
|
||||
import ButtonIcon from "../buttonIcon";
|
||||
import GroupCallMicrophoneIconMini from "../groupCall/microphoneIconMini";
|
||||
import { MovableState } from "../movableElement";
|
||||
import PeerTitle from "../peerTitle";
|
||||
import PopupElement from "../popups";
|
||||
import SetTransition from "../singleTransition";
|
||||
import makeButton from "./button";
|
||||
import CallDescriptionElement from "./description";
|
||||
import callVideoCanvasBlur from "./videoCanvasBlur";
|
||||
|
||||
const className = 'call';
|
||||
|
||||
let previousState: MovableState = {
|
||||
width: 400,
|
||||
height: 580
|
||||
};
|
||||
|
||||
export default class PopupCall extends PopupElement {
|
||||
private instance: CallInstance;
|
||||
private appAvatarsManager: AppAvatarsManager;
|
||||
private appPeersManager: AppPeersManager;
|
||||
private peerId: PeerId;
|
||||
|
||||
private description: CallDescriptionElement;
|
||||
private emojisSubtitle: HTMLElement;
|
||||
|
||||
private partyStates: HTMLElement;
|
||||
private partyMutedState: HTMLElement;
|
||||
|
||||
private firstButtonsRow: HTMLElement;
|
||||
private secondButtonsRow: HTMLElement;
|
||||
|
||||
private declineI18nElement: I18n.IntlElement;
|
||||
|
||||
private makeButton: (options: Parameters<typeof makeButton>[2]) => HTMLElement;
|
||||
private btnAccept: HTMLElement;
|
||||
private btnDecline: HTMLElement;
|
||||
private btnVideo: HTMLElement;
|
||||
private btnScreen: HTMLElement;
|
||||
private btnMute: HTMLElement;
|
||||
private btnFullScreen: HTMLButtonElement;
|
||||
private btnExitFullScreen: HTMLButtonElement;
|
||||
|
||||
private movablePanel: MovablePanel;
|
||||
private microphoneIcon: GroupCallMicrophoneIconMini;
|
||||
private muteI18nElement: I18n.IntlElement;
|
||||
|
||||
private videoContainers: {
|
||||
input?: HTMLElement,
|
||||
output?: HTMLElement
|
||||
};
|
||||
|
||||
private controlsHover: ControlsHover;
|
||||
|
||||
constructor(options: {
|
||||
appAvatarsManager: AppAvatarsManager,
|
||||
appPeersManager: AppPeersManager,
|
||||
instance: CallInstance
|
||||
}) {
|
||||
super('popup-call', undefined, {
|
||||
withoutOverlay: true,
|
||||
closable: true
|
||||
});
|
||||
|
||||
safeAssign(this, options);
|
||||
|
||||
this.videoContainers = {};
|
||||
|
||||
const {container, listenerSetter, instance} = this;
|
||||
container.classList.add(className, 'night');
|
||||
|
||||
const avatarContainer = document.createElement('div');
|
||||
avatarContainer.classList.add(className + '-avatar');
|
||||
|
||||
const peerId = this.peerId = this.instance.interlocutorUserId.toPeerId();
|
||||
const photo = this.appPeersManager.getPeerPhoto(peerId);
|
||||
this.appAvatarsManager.putAvatar(avatarContainer, peerId, photo, 'photo_big');
|
||||
|
||||
const title = new PeerTitle({
|
||||
peerId
|
||||
}).element;
|
||||
|
||||
title.classList.add(className + '-title');
|
||||
|
||||
const subtitle = document.createElement('div');
|
||||
subtitle.classList.add(className + '-subtitle');
|
||||
|
||||
const description = this.description = new CallDescriptionElement(subtitle);
|
||||
|
||||
const emojisSubtitle = this.emojisSubtitle = document.createElement('div');
|
||||
emojisSubtitle.classList.add(className + '-emojis');
|
||||
|
||||
container.append(avatarContainer, title, subtitle, emojisSubtitle);
|
||||
|
||||
this.btnFullScreen = ButtonIcon('fullscreen');
|
||||
this.btnExitFullScreen = ButtonIcon('smallscreen hide');
|
||||
attachClickEvent(this.btnFullScreen, this.onFullScreenClick, {listenerSetter});
|
||||
attachClickEvent(this.btnExitFullScreen, () => cancelFullScreen(), {listenerSetter});
|
||||
addFullScreenListener(this.container, this.onFullScreenChange, listenerSetter);
|
||||
this.header.prepend(this.btnExitFullScreen);
|
||||
this.header.append(this.btnFullScreen);
|
||||
|
||||
this.partyStates = document.createElement('div');
|
||||
this.partyStates.classList.add(className + '-party-states');
|
||||
|
||||
this.partyMutedState = document.createElement('div');
|
||||
this.partyMutedState.classList.add(className + '-party-state');
|
||||
const stateText = i18n('VoipUserMicrophoneIsOff', [new PeerTitle({peerId, onlyFirstName: true}).element]);
|
||||
stateText.classList.add(className + '-party-state-text');
|
||||
const mutedIcon = new GroupCallMicrophoneIconMini(false, true);
|
||||
mutedIcon.setState(false, false);
|
||||
this.partyMutedState.append(
|
||||
mutedIcon.container,
|
||||
stateText
|
||||
);
|
||||
|
||||
this.partyStates.append(this.partyMutedState);
|
||||
this.container.append(this.partyStates);
|
||||
|
||||
this.makeButton = makeButton.bind(null, className, this.listenerSetter);
|
||||
this.constructFirstButtons();
|
||||
this.constructSecondButtons();
|
||||
|
||||
listenerSetter.add(instance)('state', () => {
|
||||
this.updateInstance();
|
||||
});
|
||||
|
||||
listenerSetter.add(instance)('mediaState', () => {
|
||||
this.updateInstance();
|
||||
});
|
||||
|
||||
this.movablePanel = new MovablePanel({
|
||||
listenerSetter,
|
||||
movableOptions: {
|
||||
minWidth: 400,
|
||||
minHeight: 580,
|
||||
element: this.element,
|
||||
verifyTouchTarget: (e) => {
|
||||
const target = e.target;
|
||||
if(findUpClassName(target, 'call-button') ||
|
||||
findUpClassName(target, 'btn-icon') ||
|
||||
isFullScreen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
// onResize: () => this.toggleBigLayout(),
|
||||
previousState
|
||||
});
|
||||
|
||||
const controlsHover = this.controlsHover = new ControlsHover();
|
||||
controlsHover.setup({
|
||||
element: this.container,
|
||||
listenerSetter: this.listenerSetter,
|
||||
showOnLeaveToClassName: 'call-buttons'
|
||||
});
|
||||
controlsHover.showControls(false);
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
const {movablePanel} = this;
|
||||
previousState = movablePanel.state;
|
||||
|
||||
this.microphoneIcon.destroy();
|
||||
|
||||
movablePanel.destroy();
|
||||
});
|
||||
|
||||
this.updateInstance();
|
||||
}
|
||||
|
||||
private constructFirstButtons() {
|
||||
const buttons = this.firstButtonsRow = document.createElement('div');
|
||||
buttons.classList.add(className + '-buttons', 'is-first');
|
||||
|
||||
const toggleDisability = toggleClassName.bind(null, 'btn-disabled');
|
||||
|
||||
const btnVideo = this.btnVideo = this.makeButton({
|
||||
text: 'Call.Camera',
|
||||
icon: 'videocamera_filled',
|
||||
callback: () => {
|
||||
const toggle = toggleDisability([btnVideo, btnScreen], true);
|
||||
this.instance.toggleVideoSharing().finally(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
const btnScreen = this.btnScreen = this.makeButton({
|
||||
text: 'Call.Screen',
|
||||
icon: 'sharescreen_filled',
|
||||
callback: () => {
|
||||
const toggle = toggleDisability([btnVideo, btnScreen], true);
|
||||
this.instance.toggleScreenSharing().finally(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
if(!IS_SCREEN_SHARING_SUPPORTED) {
|
||||
btnScreen.classList.add('hide');
|
||||
this.container.classList.add('no-screen');
|
||||
}
|
||||
|
||||
this.muteI18nElement = new I18n.IntlElement({
|
||||
key: 'Call.Mute'
|
||||
});
|
||||
const btnMute = this.btnMute = this.makeButton({
|
||||
text: this.muteI18nElement.element,
|
||||
callback: () => {
|
||||
this.instance.toggleMuted();
|
||||
}
|
||||
});
|
||||
|
||||
const microphoneIcon = this.microphoneIcon = new GroupCallMicrophoneIconMini(true, true);
|
||||
btnMute.firstElementChild.append(microphoneIcon.container);
|
||||
|
||||
// btnVideo.classList.add('disabled');
|
||||
// btnScreen.classList.add('disabled');
|
||||
|
||||
buttons.append(btnVideo, btnScreen, btnMute);
|
||||
this.container.append(buttons);
|
||||
}
|
||||
|
||||
private constructSecondButtons() {
|
||||
const buttons = this.secondButtonsRow = document.createElement('div');
|
||||
buttons.classList.add(className + '-buttons', 'is-second');
|
||||
|
||||
this.declineI18nElement = new I18n.IntlElement({
|
||||
key: 'Call.Decline'
|
||||
});
|
||||
const btnDecline = this.btnDecline = this.makeButton({
|
||||
text: this.declineI18nElement.element,
|
||||
icon: 'endcall_filled',
|
||||
callback: () => {
|
||||
this.instance.hangUp('phoneCallDiscardReasonHangup');
|
||||
},
|
||||
isDanger: true
|
||||
});
|
||||
|
||||
const btnAccept = this.btnAccept = this.makeButton({
|
||||
text: 'Call.Accept',
|
||||
icon: 'phone',
|
||||
callback: () => {
|
||||
this.instance.acceptCall();
|
||||
},
|
||||
isConfirm: true,
|
||||
});
|
||||
|
||||
buttons.append(btnDecline, btnAccept);
|
||||
this.container.append(buttons);
|
||||
}
|
||||
|
||||
private onFullScreenClick = () => {
|
||||
requestFullScreen(this.container);
|
||||
};
|
||||
|
||||
private onFullScreenChange = () => {
|
||||
const isFull = isFullScreen();
|
||||
|
||||
const {btnFullScreen, btnExitFullScreen} = this;
|
||||
|
||||
const wasFullScreen = this.container.classList.contains('is-full-screen');
|
||||
this.container.classList.toggle('is-full-screen', isFull);
|
||||
btnFullScreen && btnFullScreen.classList.toggle('hide', isFull);
|
||||
btnExitFullScreen && btnExitFullScreen.classList.toggle('hide', !isFull);
|
||||
this.btnClose.classList.toggle('hide', isFull);
|
||||
|
||||
if(isFull !== wasFullScreen) {
|
||||
animationIntersector.checkAnimations(isFull);
|
||||
|
||||
rootScope.setThemeColor(isFull ? '#000000' : undefined);
|
||||
}
|
||||
};
|
||||
|
||||
private createVideoContainer(video: HTMLVideoElement) {
|
||||
const _className = className + '-video';
|
||||
const container = document.createElement('div');
|
||||
container.classList.add(_className + '-container');
|
||||
|
||||
video.classList.add(_className);
|
||||
if(video.paused) {
|
||||
video.play();
|
||||
}
|
||||
|
||||
attachClickEvent(container, () => {
|
||||
if(!container.classList.contains('small')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const big = Object.values(this.videoContainers).find(container => !container.classList.contains('small'));
|
||||
big.classList.add('small');
|
||||
big.style.cssText = container.style.cssText;
|
||||
container.classList.remove('small');
|
||||
container.style.cssText = '';
|
||||
});
|
||||
|
||||
const canvas = callVideoCanvasBlur(video);
|
||||
canvas.classList.add(_className + '-blur');
|
||||
|
||||
container.append(canvas, video);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private updateInstance() {
|
||||
const {instance} = this;
|
||||
const {connectionState} = instance;
|
||||
if(connectionState === CALL_STATE.CLOSED) {
|
||||
if(this.container.classList.contains('is-full-screen')) {
|
||||
cancelFullScreen();
|
||||
}
|
||||
|
||||
this.btnVideo.classList.add('disabled');
|
||||
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const isPendingIncoming = !instance.isOutgoing && connectionState === CALL_STATE.PENDING;
|
||||
this.declineI18nElement.compareAndUpdate({
|
||||
key: connectionState === CALL_STATE.PENDING ? 'Call.Decline' : 'Call.End'
|
||||
});
|
||||
this.btnAccept.classList.toggle('disable', !isPendingIncoming);
|
||||
this.btnAccept.classList.toggle('hide-me', !isPendingIncoming);
|
||||
this.container.classList.toggle('two-button-rows', isPendingIncoming);
|
||||
|
||||
const isMuted = instance.isMuted;
|
||||
const onFrame = () => {
|
||||
this.btnMute.firstElementChild.classList.toggle('active', isMuted);
|
||||
};
|
||||
|
||||
const player = this.microphoneIcon.getItem().player;
|
||||
this.microphoneIcon.setState(!isMuted, !isMuted, onFrame);
|
||||
if(!player) {
|
||||
onFrame();
|
||||
}
|
||||
|
||||
this.muteI18nElement.compareAndUpdate({
|
||||
key: isMuted ? 'VoipUnmute' : 'Call.Mute'
|
||||
});
|
||||
|
||||
const isSharingVideo = instance.isSharingVideo;
|
||||
this.btnVideo.firstElementChild.classList.toggle('active', isSharingVideo);
|
||||
|
||||
const isSharingScreen = instance.isSharingScreen;
|
||||
this.btnScreen.firstElementChild.classList.toggle('active', isSharingScreen);
|
||||
|
||||
const outputState = instance.getMediaState('output');
|
||||
|
||||
SetTransition(this.partyMutedState, 'is-visible', !!outputState?.muted, 300);
|
||||
|
||||
const containers = this.videoContainers;
|
||||
['input' as const, 'output' as const].forEach(type => {
|
||||
const mediaState = instance.getMediaState(type);
|
||||
const video = instance.getVideoElement(type) as HTMLVideoElement;
|
||||
const isActive = !!video && !!(mediaState && (mediaState.videoState === 'active' || mediaState.screencastState === 'active'));
|
||||
let videoContainer = containers[type];
|
||||
|
||||
if(isActive && video && !videoContainer) {
|
||||
videoContainer = containers[type] = this.createVideoContainer(video);
|
||||
this.container.append(videoContainer);
|
||||
}
|
||||
|
||||
if(!isActive && videoContainer) {
|
||||
videoContainer.remove();
|
||||
delete containers[type];
|
||||
}
|
||||
});
|
||||
|
||||
const inputVideoContainer = containers.input;
|
||||
if(inputVideoContainer) {
|
||||
const isSmall = !!containers.output;
|
||||
inputVideoContainer.classList.toggle('small', isSmall);
|
||||
|
||||
const video = instance.getVideoElement('input') as HTMLVideoElement;
|
||||
if(isSmall) {
|
||||
const mediaSize = new MediaSize(120, 80);
|
||||
const aspected = mediaSize.aspectFitted(new MediaSize(video.videoWidth, video.videoHeight));
|
||||
// inputVideoContainer.style.width = aspected.width + 'px';
|
||||
// inputVideoContainer.style.height = aspected.height + 'px';
|
||||
// const ratio = 120 / 80;
|
||||
inputVideoContainer.style.width = '120px';
|
||||
inputVideoContainer.style.height = '80px';
|
||||
} else {
|
||||
inputVideoContainer.style.cssText = '';
|
||||
}
|
||||
}
|
||||
|
||||
this.container.classList.toggle('no-video', !Object.keys(containers).length);
|
||||
|
||||
if(!this.emojisSubtitle.textContent && connectionState < CALL_STATE.EXCHANGING_KEYS) {
|
||||
Promise.resolve(instance.getEmojisFingerprint()).then(emojis => {
|
||||
this.emojisSubtitle.innerHTML = RichTextProcessor.wrapEmojiText(emojis.join(''));
|
||||
});
|
||||
}
|
||||
|
||||
this.setDescription();
|
||||
}
|
||||
|
||||
private setDescription() {
|
||||
this.description.update(this.instance);
|
||||
}
|
||||
}
|
@ -6,12 +6,12 @@
|
||||
|
||||
import attachListNavigation from "../../helpers/dom/attachListNavigation";
|
||||
import EventListenerBase from "../../helpers/eventListenerBase";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import { IS_MOBILE } from "../../environment/userAgent";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import appNavigationController, { NavigationItem } from "../appNavigationController";
|
||||
import SetTransition from "../singleTransition";
|
||||
import AutocompleteHelperController from "./autocompleteHelperController";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
|
||||
export default class AutocompleteHelper extends EventListenerBase<{
|
||||
hidden: () => void,
|
||||
|
@ -8,7 +8,7 @@ import rootScope from "../../lib/rootScope";
|
||||
//import { generatePathData } from "../../helpers/dom";
|
||||
import { MyMessage } from "../../lib/appManagers/appMessagesManager";
|
||||
import type Chat from "./chat";
|
||||
import { indexOfAndSplice } from "../../helpers/array";
|
||||
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
|
||||
|
||||
type Group = {bubble: HTMLElement, mid: number, timestamp: number}[];
|
||||
type BubbleGroup = {timestamp: number, fromId: PeerId, mid: number, group: Group};
|
||||
|
@ -18,7 +18,6 @@ import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager";
|
||||
import type { AppMessagesIdsManager } from "../../lib/appManagers/appMessagesIdsManager";
|
||||
import type Chat from "./chat";
|
||||
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
|
||||
import { getObjectKeysAndSort } from "../../helpers/object";
|
||||
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
|
||||
import { logger } from "../../lib/logger";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
@ -53,7 +52,6 @@ import DEBUG from "../../config/debug";
|
||||
import { SliceEnd } from "../../helpers/slicedArray";
|
||||
import serverTimeManager from "../../lib/mtproto/serverTimeManager";
|
||||
import PeerTitle from "../peerTitle";
|
||||
import { forEachReverse } from "../../helpers/array";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import findUpTag from "../../helpers/dom/findUpTag";
|
||||
import { toast } from "../toast";
|
||||
@ -94,6 +92,8 @@ import type { AppReactionsManager } from "../../lib/appManagers/appReactionsMana
|
||||
import RLottiePlayer from "../../lib/rlottie/rlottiePlayer";
|
||||
import { pause } from "../../helpers/schedulers/pause";
|
||||
import ScrollSaver from "../../helpers/scrollSaver";
|
||||
import getObjectKeysAndSort from "../../helpers/object/getObjectKeysAndSort";
|
||||
import forEachReverse from "../../helpers/array/forEachReverse";
|
||||
|
||||
const USE_MEDIA_TAILS = false;
|
||||
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
|
||||
|
@ -76,7 +76,6 @@ import PeerTitle from '../peerTitle';
|
||||
import { fastRaf } from '../../helpers/schedulers';
|
||||
import PopupDeleteMessages from '../popups/deleteMessages';
|
||||
import fixSafariStickyInputFocusing, { IS_STICKY_INPUT_BUGGED } from '../../helpers/dom/fixSafariStickyInputFocusing';
|
||||
import { copy } from '../../helpers/object';
|
||||
import PopupPeer from '../popups/peer';
|
||||
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
||||
import appMediaPlaybackController from '../appMediaPlaybackController';
|
||||
@ -89,9 +88,10 @@ import findUpTag from '../../helpers/dom/findUpTag';
|
||||
import toggleDisability from '../../helpers/dom/toggleDisability';
|
||||
import AvatarElement from '../avatar';
|
||||
import type { AppProfileManager } from '../../lib/appManagers/appProfileManager';
|
||||
import { indexOfAndSplice } from '../../helpers/array';
|
||||
import callbackify from '../../helpers/callbackify';
|
||||
import ChatBotCommands from './botCommands';
|
||||
import copy from '../../helpers/object/copy';
|
||||
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
|
||||
|
||||
const RECORD_MIN_TIME = 500;
|
||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||
|
@ -12,8 +12,8 @@ import { ripple } from "../ripple";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import { Message } from "../../layer";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
|
||||
const classNames: string[] = ['is-pinned-message-shown', 'is-pinned-audio-shown'];
|
||||
const CLASSNAME_BASE = 'pinned-container';
|
||||
|
@ -10,7 +10,6 @@ import DropdownHover from "../../helpers/dropdownHover";
|
||||
import { KeyboardButton, ReplyMarkup } from "../../layer";
|
||||
import RichTextProcessor from "../../lib/richtextprocessor";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import ListenerSetter, { Listener } from "../../helpers/listenerSetter";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
|
||||
@ -18,6 +17,7 @@ import findUpAsChild from "../../helpers/dom/findUpAsChild";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import { getHeavyAnimationPromise } from "../../hooks/useHeavyAnimationCheck";
|
||||
import confirmationPopup from "../confirmationPopup";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
|
||||
export default class ReplyKeyboard extends DropdownHover {
|
||||
private static BASE_CLASS = 'reply-keyboard';
|
||||
|
@ -27,7 +27,7 @@ import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import cancelSelection from "../../helpers/dom/cancelSelection";
|
||||
import getSelectedText from "../../helpers/dom/getSelectedText";
|
||||
import rootScope from "../../lib/rootScope";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import { fastRaf } from "../../helpers/schedulers";
|
||||
import replaceContent from "../../helpers/dom/replaceContent";
|
||||
import AppSearchSuper from "../appSearchSuper.";
|
||||
import isInDOM from "../../helpers/dom/isInDOM";
|
||||
@ -36,6 +36,7 @@ import { attachContextMenuListener } from "../misc";
|
||||
import { attachClickEvent, AttachClickOptions } from "../../helpers/dom/clickEvent";
|
||||
import findUpAsChild from "../../helpers/dom/findUpAsChild";
|
||||
import EventListenerBase from "../../helpers/eventListenerBase";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
|
||||
const accumulateMapSet = (map: Map<any, Set<number>>) => {
|
||||
return [...map.values()].reduce((acc, v) => acc + v.size, 0);
|
||||
|
@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import appStateManager from "../lib/appManagers/appStateManager";
|
||||
import { getDeepProperty } from "../helpers/object";
|
||||
import { ripple } from "./ripple";
|
||||
import { LangPackKey, _i18n } from "../lib/langPack";
|
||||
import getDeepProperty from "../helpers/object/getDeepProperty";
|
||||
|
||||
export type CheckboxFieldOptions = {
|
||||
text?: LangPackKey,
|
||||
|
@ -9,8 +9,8 @@ import AvatarEdit from "./avatarEdit";
|
||||
import AvatarElement from "./avatar";
|
||||
import InputField from "./inputField";
|
||||
import ListenerSetter from "../helpers/listenerSetter";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import ButtonCorner from "./buttonCorner";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
export default class EditPeer {
|
||||
public nextBtn: HTMLButtonElement;
|
||||
|
@ -8,7 +8,6 @@ import PopupElement from "../popups";
|
||||
import { hexToRgb } from "../../helpers/color";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import customProperties from "../../helpers/dom/customProperties";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import { GroupCall, GroupCallParticipant } from "../../layer";
|
||||
import type { AppChatsManager } from "../../lib/appManagers/appChatsManager";
|
||||
import type { AppGroupCallsManager } from "../../lib/appManagers/appGroupCallsManager";
|
||||
@ -28,13 +27,14 @@ import Scrollable from "../scrollable";
|
||||
import { MovableState } from "../movableElement";
|
||||
import animationIntersector from "../animationIntersector";
|
||||
import { IS_APPLE_MOBILE } from "../../environment/userAgent";
|
||||
import toggleDisability from "../../helpers/dom/toggleDisability";
|
||||
import throttle from "../../helpers/schedulers/throttle";
|
||||
import IS_SCREEN_SHARING_SUPPORTED from "../../environment/screenSharingSupport";
|
||||
import GroupCallInstance from "../../lib/calls/groupCallInstance";
|
||||
import makeButton from "../call/button";
|
||||
import MovablePanel from "../../helpers/movablePanel";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
import toggleClassName from "../../helpers/toggleClassName";
|
||||
|
||||
export enum GROUP_CALL_PARTICIPANT_MUTED_STATE {
|
||||
UNMUTED,
|
||||
@ -345,15 +345,17 @@ export default class PopupGroupCall extends PopupElement {
|
||||
this.buttonsContainer.classList.toggle('show-controls', show);
|
||||
};
|
||||
|
||||
private toggleDisability = toggleClassName.bind(null, 'btn-disabled');
|
||||
|
||||
private onVideoClick = () => {
|
||||
const toggle = toggleDisability([this.btnVideo], true);
|
||||
const toggle = this.toggleDisability([this.btnVideo], true);
|
||||
this.instance.toggleVideoSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
};
|
||||
|
||||
private onScreenClick = () => {
|
||||
const toggle = toggleDisability([this.btnScreen], true);
|
||||
const toggle = this.toggleDisability([this.btnScreen], true);
|
||||
this.instance.toggleScreenSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
|
@ -8,7 +8,7 @@ import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import ControlsHover from "../../helpers/dom/controlsHover";
|
||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
import { GroupCallParticipant } from "../../layer";
|
||||
import { AppGroupCallsManager, GroupCallOutputSource } from "../../lib/appManagers/appGroupCallsManager";
|
||||
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
|
||||
|
@ -10,7 +10,7 @@ import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||
import { addFullScreenListener, isFullScreen } from "../../helpers/dom/fullScreen";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
import noop from "../../helpers/noop";
|
||||
import { safeAssign } from "../../helpers/object";
|
||||
import safeAssign from "../../helpers/object/safeAssign";
|
||||
import ScrollableLoader from "../../helpers/scrollableLoader";
|
||||
import { GroupCallParticipant } from "../../layer";
|
||||
import type { AppChatsManager } from "../../lib/appManagers/appChatsManager";
|
||||
|
@ -5,7 +5,7 @@ For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
import { accumulate } from "../helpers/array";
|
||||
import accumulate from "../helpers/array/accumulate";
|
||||
import { clamp } from "../helpers/number";
|
||||
|
||||
type Size = {w: number, h: number};
|
||||
|
@ -6,8 +6,9 @@
|
||||
|
||||
import { logger, LogTypes } from "../lib/logger";
|
||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||
import { findAndSpliceAll, indexOfAndSplice } from "../helpers/array";
|
||||
import throttle from "../helpers/schedulers/throttle";
|
||||
import findAndSpliceAll from "../helpers/array/findAndSpliceAll";
|
||||
import indexOfAndSplice from "../helpers/array/indexOfAndSplice";
|
||||
|
||||
type LazyLoadElementBase = {
|
||||
load: () => Promise<any>
|
||||
|
@ -5,11 +5,10 @@
|
||||
*/
|
||||
|
||||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||||
import { isFullScreen } from "../helpers/dom/fullScreen";
|
||||
import EventListenerBase from "../helpers/eventListenerBase";
|
||||
import mediaSizes from "../helpers/mediaSizes";
|
||||
import { clamp } from "../helpers/number";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
import windowSize from "../helpers/windowSize";
|
||||
import SwipeHandler from "./swipeHandler";
|
||||
|
||||
|
@ -16,8 +16,8 @@ import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEve
|
||||
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import EventListenerBase from "../../helpers/eventListenerBase";
|
||||
import { indexOfAndSplice } from "../../helpers/array";
|
||||
import { addFullScreenListener, getFullScreenElement, isFullScreen } from "../../helpers/dom/fullScreen";
|
||||
import { addFullScreenListener, getFullScreenElement } from "../../helpers/dom/fullScreen";
|
||||
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
|
||||
|
||||
export type PopupButton = {
|
||||
text?: string,
|
||||
|
@ -7,10 +7,10 @@
|
||||
import { CancellablePromise } from "../helpers/cancellablePromise";
|
||||
import SetTransition from "./singleTransition";
|
||||
import { fastRaf } from "../helpers/schedulers";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
||||
import isInDOM from "../helpers/dom/isInDOM";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
const TRANSITION_TIME = 200;
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import getDeepProperty from "../helpers/object/getDeepProperty";
|
||||
import appStateManager from "../lib/appManagers/appStateManager";
|
||||
import { getDeepProperty } from "../helpers/object";
|
||||
import { LangPackKey, _i18n } from "../lib/langPack";
|
||||
|
||||
export default class RadioField {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import { clamp } from "../helpers/number";
|
||||
import attachGrabListeners, { GrabEvent } from "../helpers/dom/attachGrabListeners";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
export default class RangeSelector {
|
||||
public container: HTMLDivElement;
|
||||
|
@ -40,7 +40,6 @@ import replaceContent from "../../helpers/dom/replaceContent";
|
||||
import sessionStorage from "../../lib/sessionStorage";
|
||||
import { attachClickEvent, CLICK_EVENT_NAME } from "../../helpers/dom/clickEvent";
|
||||
import { closeBtnMenu } from "../misc";
|
||||
import { indexOfAndSplice } from "../../helpers/array";
|
||||
import ButtonIcon from "../buttonIcon";
|
||||
import confirmationPopup from "../confirmationPopup";
|
||||
import IS_GEOLOCATION_SUPPORTED from "../../environment/geolocationSupport";
|
||||
@ -48,6 +47,7 @@ import type SortedUserList from "../sortedUserList";
|
||||
import Button, { ButtonOptions } from "../button";
|
||||
import noop from "../../helpers/noop";
|
||||
import { ripple } from "../ripple";
|
||||
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
|
||||
|
||||
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import { SliderSuperTab } from "../../slider";
|
||||
import AppSelectPeers from "../../appSelectPeers";
|
||||
import { putPreloader, setButtonLoader } from "../../misc";
|
||||
import { setButtonLoader } from "../../misc";
|
||||
import { LangPackKey, _i18n } from "../../../lib/langPack";
|
||||
import ButtonCorner from "../../buttonCorner";
|
||||
|
||||
|
@ -12,7 +12,7 @@ import { attachClickEvent } from "../../../helpers/dom/clickEvent";
|
||||
import findUpClassName from "../../../helpers/dom/findUpClassName";
|
||||
import { requestFile } from "../../../helpers/files";
|
||||
import highlightningColor from "../../../helpers/highlightningColor";
|
||||
import { copy } from "../../../helpers/object";
|
||||
import copy from "../../../helpers/object/copy";
|
||||
import sequentialDom from "../../../helpers/sequentialDom";
|
||||
import ChatBackgroundGradientRenderer from "../../chat/gradientRenderer";
|
||||
import { AccountWallPapers, PhotoSize, WallPaper } from "../../../layer";
|
||||
|
@ -4,7 +4,6 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { deepEqual, copy } from "../../../helpers/object";
|
||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
|
||||
import lottieLoader, { LottieLoader } from "../../../lib/rlottie/lottieLoader";
|
||||
@ -22,6 +21,8 @@ import { i18n, i18n_, LangPackKey } from "../../../lib/langPack";
|
||||
import { SettingSection } from "..";
|
||||
import PopupPeer from "../../popups/peer";
|
||||
import RLottiePlayer from "../../../lib/rlottie/rlottiePlayer";
|
||||
import copy from "../../../helpers/object/copy";
|
||||
import deepEqual from "../../../helpers/object/deepEqual";
|
||||
|
||||
const MAX_FOLDER_NAME_LENGTH = 12;
|
||||
|
||||
|
@ -9,7 +9,6 @@ import AppSelectPeers from "../../appSelectPeers";
|
||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
|
||||
import { copy } from "../../../helpers/object";
|
||||
import ButtonIcon from "../../buttonIcon";
|
||||
import CheckboxField from "../../checkboxField";
|
||||
import Button from "../../button";
|
||||
@ -19,8 +18,9 @@ import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
|
||||
import RichTextProcessor from "../../../lib/richtextprocessor";
|
||||
import { SettingSection } from "..";
|
||||
import { toast } from "../../toast";
|
||||
import { forEachReverse } from "../../../helpers/array";
|
||||
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||
import copy from "../../../helpers/object/copy";
|
||||
import forEachReverse from "../../../helpers/array/forEachReverse";
|
||||
|
||||
export default class AppIncludedChatsTab extends SliderSuperTab {
|
||||
private editFolderTab: AppEditFolderTab;
|
||||
|
@ -10,11 +10,11 @@ import CheckboxField from "../../checkboxField";
|
||||
import { InputNotifyPeer, 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";
|
||||
import { LangPackKey } from "../../../lib/langPack";
|
||||
import appStateManager from "../../../lib/appManagers/appStateManager";
|
||||
import copy from "../../../helpers/object/copy";
|
||||
|
||||
type InputNotifyKey = Exclude<InputNotifyPeer['_'], 'inputNotifyPeer'>;
|
||||
|
||||
|
@ -15,10 +15,10 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||
import { wrapSticker } from "../../wrappers";
|
||||
import appSidebarRight from "..";
|
||||
import { StickerSet, StickerSetCovered } from "../../../layer";
|
||||
import { forEachReverse } from "../../../helpers/array";
|
||||
import { i18n } from "../../../lib/langPack";
|
||||
import findUpClassName from "../../../helpers/dom/findUpClassName";
|
||||
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
|
||||
import forEachReverse from "../../../helpers/array/forEachReverse";
|
||||
|
||||
export default class AppStickersTab extends SliderSuperTab {
|
||||
private inputSearch: InputSearch;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
|
||||
import toggleDisability from "../../../helpers/dom/toggleDisability";
|
||||
import { deepEqual } from "../../../helpers/object";
|
||||
import deepEqual from "../../../helpers/object/deepEqual";
|
||||
import { ChannelParticipant } from "../../../layer";
|
||||
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||
|
@ -8,9 +8,9 @@ import { horizontalMenu } from "./horizontalMenu";
|
||||
import { TransitionSlider } from "./transition";
|
||||
import appNavigationController, { NavigationItem } from "./appNavigationController";
|
||||
import SliderSuperTab, { SliderSuperTabConstructable, SliderTab } from "./sliderTab";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
||||
import { indexOfAndSplice } from "../helpers/array";
|
||||
import indexOfAndSplice from "../helpers/array/indexOfAndSplice";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
const TRANSITION_TIME = 250;
|
||||
|
||||
|
@ -11,9 +11,9 @@ import appUsersManager from "../lib/appManagers/appUsersManager";
|
||||
import isInDOM from "../helpers/dom/isInDOM";
|
||||
import positionElementByIndex from "../helpers/dom/positionElementByIndex";
|
||||
import replaceContent from "../helpers/dom/replaceContent";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { fastRaf } from "../helpers/schedulers";
|
||||
import SortedList, { SortedElementBase } from "../helpers/sortedList";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
interface SortedUser extends SortedElementBase {
|
||||
dom: DialogDom
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import noop from "../helpers/noop";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
import { LottieAssetName } from "../lib/rlottie/lottieLoader";
|
||||
import RLottieIcon, { RLottieIconItemPartOptions, RLottieIconItemPart } from "../lib/rlottie/rlottieIcon";
|
||||
import { RLottieColor } from "../lib/rlottie/rlottiePlayer";
|
||||
|
@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import { safeAssign } from "../helpers/object";
|
||||
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import safeAssign from "../helpers/object/safeAssign";
|
||||
|
||||
const getEvent = (e: TouchEvent | MouseEvent) => {
|
||||
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent;
|
||||
|
@ -25,9 +25,10 @@ import CALL_STATE from "../lib/calls/callState";
|
||||
import replaceContent from "../helpers/dom/replaceContent";
|
||||
import PeerTitle from "./peerTitle";
|
||||
import CallDescriptionElement from "./call/description";
|
||||
// import PopupCall from "./call";
|
||||
import PopupCall from "./call";
|
||||
import type { AppAvatarsManager } from "../lib/appManagers/appAvatarsManager";
|
||||
import GroupCallMicrophoneIconMini from "./groupCall/microphoneIconMini";
|
||||
import CallInstance from "../lib/calls/callInstance";
|
||||
|
||||
function convertCallStateToGroupState(state: CALL_STATE, isMuted: boolean) {
|
||||
switch(state) {
|
||||
@ -255,13 +256,13 @@ export default class TopbarCall {
|
||||
appPeersManager: this.appPeersManager,
|
||||
appChatsManager: this.appChatsManager
|
||||
}).show();
|
||||
}/* else if(this.instance instanceof CallInstance) {
|
||||
} else if(this.instance instanceof CallInstance) {
|
||||
new PopupCall({
|
||||
appAvatarsManager: this.appAvatarsManager,
|
||||
appPeersManager: this.appPeersManager,
|
||||
instance: this.instance
|
||||
}).show();
|
||||
} */
|
||||
}
|
||||
}, {listenerSetter});
|
||||
|
||||
container.append(left, center, right);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import IS_WEBRTC_SUPPORTED from "./webrtcSupport";
|
||||
|
||||
const IS_CALL_SUPPORTED = IS_WEBRTC_SUPPORTED && false;
|
||||
const IS_CALL_SUPPORTED = IS_WEBRTC_SUPPORTED;
|
||||
|
||||
export default IS_CALL_SUPPORTED;
|
||||
|
@ -19,71 +19,16 @@ export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
|
||||
return result;
|
||||
} */
|
||||
|
||||
export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue);
|
||||
|
||||
export function indexOfAndSplice<T>(array: Array<T>, item: T) {
|
||||
const idx = array.indexOf(item);
|
||||
const spliced = idx !== -1 && array.splice(idx, 1);
|
||||
return spliced && spliced[0];
|
||||
}
|
||||
export {};
|
||||
|
||||
|
||||
export function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: number, arr: typeof array) => boolean) {
|
||||
const out: typeof array = [];
|
||||
let idx = -1;
|
||||
while((idx = array.findIndex(verify)) !== -1) {
|
||||
out.push(array.splice(idx, 1)[0]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export function forEachReverse<T>(array: Array<T>, callback: (value: T, index?: number, array?: Array<T>) => void) {
|
||||
for(let length = array.length, i = length - 1; i >= 0; --i) {
|
||||
callback(array[i], i, array);
|
||||
}
|
||||
};
|
||||
|
||||
export function insertInDescendSortedArray<T extends {[smth in K]?: number}, K extends keyof T>(array: Array<T>, element: T, property: K, pos?: number) {
|
||||
const sortProperty: number = element[property];
|
||||
|
||||
if(pos === undefined) {
|
||||
pos = array.indexOf(element);
|
||||
}
|
||||
|
||||
if(pos !== -1) {
|
||||
const prev = array[pos - 1];
|
||||
const next = array[pos + 1];
|
||||
if((!prev || prev[property] >= sortProperty) && (!next || next[property] <= sortProperty)) {
|
||||
// console.warn('same pos', pos, sortProperty, prev, next);
|
||||
return pos;
|
||||
}
|
||||
|
||||
array.splice(pos, 1);
|
||||
}
|
||||
|
||||
const len = array.length;
|
||||
if(!len || sortProperty <= array[len - 1][property]) {
|
||||
return array.push(element) - 1;
|
||||
} else if(sortProperty >= array[0][property]) {
|
||||
array.unshift(element);
|
||||
return 0;
|
||||
} else {
|
||||
for(let i = 0; i < len; i++) {
|
||||
if(sortProperty > array[i][property]) {
|
||||
array.splice(i, 0, element);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.error('wtf', array, element);
|
||||
return array.indexOf(element);
|
||||
}
|
||||
|
||||
export function filterUnique<T extends Array<any>>(arr: T): T {
|
||||
return [...new Set(arr)] as T;
|
||||
}
|
||||
|
||||
export function flatten<T>(arr: T[][]): T[] {
|
||||
return arr.reduce((acc, val) => (acc.push(...val), acc), []);
|
||||
}
|
||||
|
3
src/helpers/array/accumulate.ts
Normal file
3
src/helpers/array/accumulate.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default function accumulate(arr: number[], initialValue: number) {
|
||||
return arr.reduce((acc, value) => acc + value, initialValue);
|
||||
}
|
3
src/helpers/array/filterUnique.ts
Normal file
3
src/helpers/array/filterUnique.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default function filterUnique<T extends Array<any>>(arr: T): T {
|
||||
return [...new Set(arr)] as T;
|
||||
}
|
9
src/helpers/array/findAndSpliceAll.ts
Normal file
9
src/helpers/array/findAndSpliceAll.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export default function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: number, arr: typeof array) => boolean) {
|
||||
const out: typeof array = [];
|
||||
let idx = -1;
|
||||
while((idx = array.findIndex(verify)) !== -1) {
|
||||
out.push(array.splice(idx, 1)[0]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
3
src/helpers/array/flatten.ts
Normal file
3
src/helpers/array/flatten.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default function flatten<T>(arr: T[][]): T[] {
|
||||
return arr.reduce((acc, val) => (acc.push(...val), acc), []);
|
||||
}
|
5
src/helpers/array/forEachReverse.ts
Normal file
5
src/helpers/array/forEachReverse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default function forEachReverse<T>(array: Array<T>, callback: (value: T, index?: number, array?: Array<T>) => void) {
|
||||
for(let length = array.length, i = length - 1; i >= 0; --i) {
|
||||
callback(array[i], i, array);
|
||||
}
|
||||
};
|
5
src/helpers/array/indexOfAndSplice.ts
Normal file
5
src/helpers/array/indexOfAndSplice.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default function indexOfAndSplice<T>(array: Array<T>, item: T) {
|
||||
const idx = array.indexOf(item);
|
||||
const spliced = idx !== -1 && array.splice(idx, 1);
|
||||
return spliced && spliced[0];
|
||||
}
|
35
src/helpers/array/insertInDescendSortedArray.ts
Normal file
35
src/helpers/array/insertInDescendSortedArray.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export default function insertInDescendSortedArray<T extends {[smth in K]?: number}, K extends keyof T>(array: Array<T>, element: T, property: K, pos?: number) {
|
||||
const sortProperty: number = element[property];
|
||||
|
||||
if(pos === undefined) {
|
||||
pos = array.indexOf(element);
|
||||
if(pos !== -1) {
|
||||
const prev = array[pos - 1];
|
||||
const next = array[pos + 1];
|
||||
if((!prev || prev[property] >= sortProperty) && (!next || next[property] <= sortProperty)) {
|
||||
// console.warn('same pos', pos, sortProperty, prev, next);
|
||||
return pos;
|
||||
}
|
||||
|
||||
array.splice(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const len = array.length;
|
||||
if(!len || sortProperty <= array[len - 1][property]) {
|
||||
return array.push(element) - 1;
|
||||
} else if(sortProperty >= array[0][property]) {
|
||||
array.unshift(element);
|
||||
return 0;
|
||||
} else {
|
||||
for(let i = 0; i < len; i++) {
|
||||
if(sortProperty > array[i][property]) {
|
||||
array.splice(i, 0, element);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.error('wtf', array, element);
|
||||
return array.indexOf(element);
|
||||
}
|
@ -9,6 +9,7 @@ const ASSETS_PATH = 'assets/audio/';
|
||||
export default class AudioAssetPlayer<AssetName extends string> {
|
||||
private audio: HTMLAudioElement;
|
||||
private tempId: number;
|
||||
private assetName: AssetName;
|
||||
|
||||
constructor(private assets: AssetName[]) {
|
||||
this.tempId = 0;
|
||||
@ -16,7 +17,8 @@ export default class AudioAssetPlayer<AssetName extends string> {
|
||||
|
||||
public playSound(name: AssetName, loop = false) {
|
||||
++this.tempId;
|
||||
|
||||
this.assetName = name;
|
||||
|
||||
try {
|
||||
const audio = this.createAudio();
|
||||
audio.autoplay = true;
|
||||
@ -28,6 +30,12 @@ export default class AudioAssetPlayer<AssetName extends string> {
|
||||
}
|
||||
}
|
||||
|
||||
public playSoundIfDifferent(name: AssetName, loop?: boolean) {
|
||||
if(this.assetName !== name) {
|
||||
this.playSound(name, loop);
|
||||
}
|
||||
}
|
||||
|
||||
public createAudio() {
|
||||
let {audio} = this;
|
||||
if(audio) {
|
||||
@ -40,7 +48,11 @@ export default class AudioAssetPlayer<AssetName extends string> {
|
||||
}
|
||||
|
||||
public stopSound() {
|
||||
this.audio?.pause();
|
||||
if(!this.audio) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.audio.pause();
|
||||
}
|
||||
|
||||
public cancelDelayedPlay() {
|
||||
|
9
src/helpers/bigInt/bigIntConversion.ts
Normal file
9
src/helpers/bigInt/bigIntConversion.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import bigInt from 'big-integer';
|
||||
|
||||
export function bigIntFromBytes(bytes: Uint8Array | number[], base = 256) {
|
||||
return bigInt.fromArray(bytes instanceof Uint8Array ? [...bytes] : bytes, base);
|
||||
}
|
||||
|
||||
export function bigIntToBytes(bigInt: bigInt.BigInteger) {
|
||||
return new Uint8Array(bigInt.toArray(256).value);
|
||||
}
|
13
src/helpers/bigInt/bigIntRandom.ts
Normal file
13
src/helpers/bigInt/bigIntRandom.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import bigInt from "big-integer";
|
||||
import { nextRandomUint } from "../random";
|
||||
|
||||
export default function bigIntRandom(min: bigInt.BigNumber, max: bigInt.BigNumber) {
|
||||
return bigInt.randBetween(min, max, () => {
|
||||
return nextRandomUint(32) / 0xFFFFFFFF;
|
||||
/* const bits = 32;
|
||||
const randomBytes = new Uint8Array(bits / 8);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
const r = bigIntFromBytes(randomBytes).mod(bigInt(2).pow(bits));
|
||||
return r.toJSNumber(); */
|
||||
});
|
||||
}
|
@ -9,91 +9,7 @@
|
||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export function bytesToHex(bytes: ArrayLike<number>) {
|
||||
const length = bytes.length;
|
||||
const arr: string[] = new Array(length);
|
||||
for(let i = 0; i < length; ++i) {
|
||||
arr[i] = (bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16);
|
||||
}
|
||||
return arr.join('');
|
||||
}
|
||||
|
||||
export function bytesFromHex(hexString: string) {
|
||||
const len = hexString.length;
|
||||
const bytes = new Uint8Array(Math.ceil(len / 2));
|
||||
let start = 0;
|
||||
|
||||
if(len % 2) { // read 0x581 as 0x0581
|
||||
bytes[start++] = parseInt(hexString.charAt(0), 16);
|
||||
}
|
||||
|
||||
for(let i = start; i < len; i += 2) {
|
||||
bytes[start++] = parseInt(hexString.substr(i, 2), 16);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function bytesToBase64(bytes: number[] | Uint8Array) {
|
||||
let mod3: number;
|
||||
let result = '';
|
||||
|
||||
for(let nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; ++nIdx) {
|
||||
mod3 = nIdx % 3;
|
||||
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
|
||||
if(mod3 === 2 || nLen - nIdx === 1) {
|
||||
result += String.fromCharCode(
|
||||
uint6ToBase64(nUint24 >>> 18 & 63),
|
||||
uint6ToBase64(nUint24 >>> 12 & 63),
|
||||
uint6ToBase64(nUint24 >>> 6 & 63),
|
||||
uint6ToBase64(nUint24 & 63)
|
||||
);
|
||||
nUint24 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result.replace(/A(?=A$|$)/g, '=');
|
||||
}
|
||||
|
||||
export function uint6ToBase64(nUint6: number) {
|
||||
return nUint6 < 26
|
||||
? nUint6 + 65
|
||||
: nUint6 < 52
|
||||
? nUint6 + 71
|
||||
: nUint6 < 62
|
||||
? nUint6 - 4
|
||||
: nUint6 === 62
|
||||
? 43
|
||||
: nUint6 === 63
|
||||
? 47
|
||||
: 65;
|
||||
}
|
||||
|
||||
export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) {
|
||||
const len = bytes1.length;
|
||||
if(len !== bytes2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(let i = 0; i < len; ++i) {
|
||||
if(bytes1[i] !== bytes2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function bytesXor(bytes1: Uint8Array, bytes2: Uint8Array) {
|
||||
const len = bytes1.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for(let i = 0; i < len; ++i) {
|
||||
bytes[i] = bytes1[i] ^ bytes2[i];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
export {};
|
||||
|
||||
/* export function bytesToArrayBuffer(b: number[]) {
|
||||
return (new Uint8Array(b)).buffer;
|
||||
@ -111,16 +27,6 @@ export function convertToArrayBuffer(bytes: any | ArrayBuffer | Uint8Array) {
|
||||
return bytesToArrayBuffer(bytes);
|
||||
} */
|
||||
|
||||
export function convertToUint8Array(bytes: Uint8Array | ArrayBuffer | number[] | string): Uint8Array {
|
||||
if(bytes instanceof Uint8Array) {
|
||||
return bytes;
|
||||
} else if(typeof(bytes) === 'string') {
|
||||
return new TextEncoder().encode(bytes);
|
||||
}
|
||||
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
/* export function bytesFromArrayBuffer(buffer: ArrayBuffer) {
|
||||
const len = buffer.byteLength;
|
||||
const byteView = new Uint8Array(buffer);
|
||||
@ -143,40 +49,6 @@ export function bufferConcat(buffer1: any, buffer2: any) {
|
||||
return tmp.buffer;
|
||||
} */
|
||||
|
||||
export function bufferConcats(...args: (ArrayBuffer | Uint8Array | number[])[]) {
|
||||
const length = args.reduce((acc, v) => acc + ((v as ArrayBuffer).byteLength || (v as Uint8Array).length), 0);
|
||||
|
||||
const tmp = new Uint8Array(length);
|
||||
|
||||
let lastLength = 0;
|
||||
args.forEach(b => {
|
||||
tmp.set(b instanceof ArrayBuffer ? new Uint8Array(b) : b, lastLength);
|
||||
lastLength += (b as ArrayBuffer).byteLength || (b as Uint8Array).length;
|
||||
});
|
||||
|
||||
return tmp/* .buffer */;
|
||||
}
|
||||
|
||||
export function bytesFromWordss(input: Uint32Array) {
|
||||
const o = new Uint8Array(input.byteLength);
|
||||
for(let i = 0, length = input.length * 4; i < length; ++i) {
|
||||
o[i] = ((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
export function bytesToWordss(input: Parameters<typeof convertToUint8Array>[0]) {
|
||||
const bytes = convertToUint8Array(input);
|
||||
|
||||
const words: number[] = [];
|
||||
for(let i = 0, len = bytes.length; i < len; ++i) {
|
||||
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
|
||||
return new Uint32Array(words);
|
||||
}
|
||||
|
||||
// * https://stackoverflow.com/a/52827031
|
||||
/* export const isBigEndian = (() => {
|
||||
const array = new Uint8Array(4);
|
||||
|
34
src/helpers/bytes/addPadding.ts
Normal file
34
src/helpers/bytes/addPadding.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import bufferConcats from "./bufferConcats";
|
||||
|
||||
export default function addPadding<T extends number[] | ArrayBuffer | Uint8Array>(
|
||||
bytes: T,
|
||||
blockSize: number = 16,
|
||||
zeroes?: boolean,
|
||||
blockSizeAsTotalLength = false,
|
||||
prepend = false
|
||||
): T {
|
||||
const len = (bytes as ArrayBuffer).byteLength || (bytes as Uint8Array).length;
|
||||
const needPadding = blockSizeAsTotalLength ? blockSize - len : blockSize - (len % blockSize);
|
||||
if(needPadding > 0 && needPadding < blockSize) {
|
||||
////console.log('addPadding()', len, blockSize, needPadding);
|
||||
const padding = new Uint8Array(needPadding);
|
||||
if(zeroes) {
|
||||
for(let i = 0; i < needPadding; ++i) {
|
||||
padding[i] = 0;
|
||||
}
|
||||
} else {
|
||||
padding.randomize();
|
||||
}
|
||||
|
||||
if(bytes instanceof ArrayBuffer) {
|
||||
return (prepend ? bufferConcats(padding, bytes) : bufferConcats(bytes, padding)).buffer as T;
|
||||
} else if(bytes instanceof Uint8Array) {
|
||||
return (prepend ? bufferConcats(padding, bytes) : bufferConcats(bytes, padding)) as T;
|
||||
} else {
|
||||
// @ts-ignore
|
||||
return (prepend ? [...padding].concat(bytes) : bytes.concat([...padding])) as T;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
13
src/helpers/bytes/bufferConcats.ts
Normal file
13
src/helpers/bytes/bufferConcats.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export default function bufferConcats(...args: (ArrayBuffer | Uint8Array | number[])[]) {
|
||||
const length = args.reduce((acc, v) => acc + ((v as ArrayBuffer).byteLength || (v as Uint8Array).length), 0);
|
||||
|
||||
const tmp = new Uint8Array(length);
|
||||
|
||||
let lastLength = 0;
|
||||
args.forEach(b => {
|
||||
tmp.set(b instanceof ArrayBuffer ? new Uint8Array(b) : b, lastLength);
|
||||
lastLength += (b as ArrayBuffer).byteLength || (b as Uint8Array).length;
|
||||
});
|
||||
|
||||
return tmp/* .buffer */;
|
||||
}
|
14
src/helpers/bytes/bytesCmp.ts
Normal file
14
src/helpers/bytes/bytesCmp.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) {
|
||||
const len = bytes1.length;
|
||||
if(len !== bytes2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(let i = 0; i < len; ++i) {
|
||||
if(bytes1[i] !== bytes2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
15
src/helpers/bytes/bytesFromHex.ts
Normal file
15
src/helpers/bytes/bytesFromHex.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export default function bytesFromHex(hexString: string) {
|
||||
const len = hexString.length;
|
||||
const bytes = new Uint8Array(Math.ceil(len / 2));
|
||||
let start = 0;
|
||||
|
||||
if(len % 2) { // read 0x581 as 0x0581
|
||||
bytes[start++] = parseInt(hexString.charAt(0), 16);
|
||||
}
|
||||
|
||||
for(let i = start; i < len; i += 2) {
|
||||
bytes[start++] = parseInt(hexString.substr(i, 2), 16);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
8
src/helpers/bytes/bytesFromWordss.ts
Normal file
8
src/helpers/bytes/bytesFromWordss.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export default function bytesFromWordss(input: Uint32Array) {
|
||||
const o = new Uint8Array(input.byteLength);
|
||||
for(let i = 0, length = input.length * 4; i < length; ++i) {
|
||||
o[i] = ((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
9
src/helpers/bytes/bytesModPow.ts
Normal file
9
src/helpers/bytes/bytesModPow.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { bigIntFromBytes, bigIntToBytes } from '../bigInt/bigIntConversion';
|
||||
|
||||
export default function bytesModPow(bytes: number[] | Uint8Array, exp: number[] | Uint8Array, mod: number[] | Uint8Array) {
|
||||
const bytesBigInt = bigIntFromBytes(bytes);
|
||||
const expBigInt = bigIntFromBytes(exp);
|
||||
const modBigInt = bigIntFromBytes(mod);
|
||||
const resBigInt = bytesBigInt.modPow(expBigInt, modBigInt);
|
||||
return bigIntToBytes(resBigInt);
|
||||
}
|
34
src/helpers/bytes/bytesToBase64.ts
Normal file
34
src/helpers/bytes/bytesToBase64.ts
Normal file
@ -0,0 +1,34 @@
|
||||
export default function bytesToBase64(bytes: number[] | Uint8Array) {
|
||||
let mod3: number;
|
||||
let result = '';
|
||||
|
||||
for(let nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; ++nIdx) {
|
||||
mod3 = nIdx % 3;
|
||||
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
|
||||
if(mod3 === 2 || nLen - nIdx === 1) {
|
||||
result += String.fromCharCode(
|
||||
uint6ToBase64(nUint24 >>> 18 & 63),
|
||||
uint6ToBase64(nUint24 >>> 12 & 63),
|
||||
uint6ToBase64(nUint24 >>> 6 & 63),
|
||||
uint6ToBase64(nUint24 & 63)
|
||||
);
|
||||
nUint24 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result.replace(/A(?=A$|$)/g, '=');
|
||||
}
|
||||
|
||||
export function uint6ToBase64(nUint6: number) {
|
||||
return nUint6 < 26
|
||||
? nUint6 + 65
|
||||
: nUint6 < 52
|
||||
? nUint6 + 71
|
||||
: nUint6 < 62
|
||||
? nUint6 - 4
|
||||
: nUint6 === 62
|
||||
? 43
|
||||
: nUint6 === 63
|
||||
? 47
|
||||
: 65;
|
||||
}
|
8
src/helpers/bytes/bytesToHex.ts
Normal file
8
src/helpers/bytes/bytesToHex.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export default function bytesToHex(bytes: ArrayLike<number>) {
|
||||
const length = bytes.length;
|
||||
const arr: string[] = new Array(length);
|
||||
for(let i = 0; i < length; ++i) {
|
||||
arr[i] = (bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16);
|
||||
}
|
||||
return arr.join('');
|
||||
}
|
12
src/helpers/bytes/bytesToWordss.ts
Normal file
12
src/helpers/bytes/bytesToWordss.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import convertToUint8Array from "./convertToUint8Array";
|
||||
|
||||
export default function bytesToWordss(input: Parameters<typeof convertToUint8Array>[0]) {
|
||||
const bytes = convertToUint8Array(input);
|
||||
|
||||
const words: number[] = [];
|
||||
for(let i = 0, len = bytes.length; i < len; ++i) {
|
||||
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
|
||||
}
|
||||
|
||||
return new Uint32Array(words);
|
||||
}
|
10
src/helpers/bytes/bytesXor.ts
Normal file
10
src/helpers/bytes/bytesXor.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export default function bytesXor(bytes1: Uint8Array, bytes2: Uint8Array) {
|
||||
const len = bytes1.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for(let i = 0; i < len; ++i) {
|
||||
bytes[i] = bytes1[i] ^ bytes2[i];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
9
src/helpers/bytes/convertToUint8Array.ts
Normal file
9
src/helpers/bytes/convertToUint8Array.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export default function convertToUint8Array(bytes: Uint8Array | ArrayBuffer | number[] | string): Uint8Array {
|
||||
if(bytes instanceof Uint8Array) {
|
||||
return bytes;
|
||||
} else if(typeof(bytes) === 'string') {
|
||||
return new TextEncoder().encode(bytes);
|
||||
}
|
||||
|
||||
return new Uint8Array(bytes);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport";
|
||||
import EventListenerBase from "../eventListenerBase";
|
||||
import ListenerSetter from "../listenerSetter";
|
||||
import { safeAssign } from "../object";
|
||||
import safeAssign from "../object/safeAssign";
|
||||
import findUpClassName from "./findUpClassName";
|
||||
|
||||
export default class ControlsHover extends EventListenerBase<{
|
||||
|
@ -8,8 +8,8 @@ import { attachClickEvent } from "./dom/clickEvent";
|
||||
import findUpAsChild from "./dom/findUpAsChild";
|
||||
import EventListenerBase from "./eventListenerBase";
|
||||
import ListenerSetter from "./listenerSetter";
|
||||
import { safeAssign } from "./object";
|
||||
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
|
||||
import safeAssign from "./object/safeAssign";
|
||||
|
||||
const KEEP_OPEN = false;
|
||||
const TOGGLE_TIMEOUT = 200;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import type { Message, MessageAction } from "../layer";
|
||||
import type { MyMessage } from "../lib/appManagers/appMessagesManager";
|
||||
import { forEachReverse } from "./array";
|
||||
import forEachReverse from "./array/forEachReverse";
|
||||
|
||||
export default function filterChatPhotosMessages(value: {
|
||||
count: number;
|
||||
|
12
src/helpers/gzipUncompress.ts
Normal file
12
src/helpers/gzipUncompress.ts
Normal file
@ -0,0 +1,12 @@
|
||||
//export function gzipUncompress(bytes: ArrayBuffer, toString: true): string;
|
||||
|
||||
// @ts-ignore
|
||||
import pako from 'pako/dist/pako_inflate.min.js';
|
||||
|
||||
//export function gzipUncompress(bytes: ArrayBuffer, toString?: false): Uint8Array;
|
||||
export default function gzipUncompress(bytes: ArrayBuffer, toString?: boolean): string | Uint8Array {
|
||||
//console.log(dT(), 'Gzip uncompress start');
|
||||
const result = pako.inflate(bytes, toString ? {to: 'string'} : undefined);
|
||||
//console.log(dT(), 'Gzip uncompress finish'/* , result */);
|
||||
return result;
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { forEachReverse } from "./array";
|
||||
import { safeAssign } from "./object";
|
||||
import forEachReverse from "./array/forEachReverse";
|
||||
import safeAssign from "./object/safeAssign";
|
||||
|
||||
export type ListLoaderOptions<T extends {}, P extends {}> = {
|
||||
loadMore: ListLoader<T, P>['loadMore'],
|
||||
|
7
src/helpers/long/longFromInts.ts
Normal file
7
src/helpers/long/longFromInts.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import bigInt from "big-integer";
|
||||
import intToUint from "../number/intToUint";
|
||||
|
||||
export default function longFromInts(high: number, low: number): string {
|
||||
high = intToUint(high), low = intToUint(low);
|
||||
return bigInt(high).shiftLeft(32).add(bigInt(low)).toString(10);
|
||||
}
|
11
src/helpers/long/longToBytes.ts
Normal file
11
src/helpers/long/longToBytes.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import addPadding from '../bytes/addPadding';
|
||||
import bigInt from 'big-integer';
|
||||
import { bigIntToBytes } from '../bigInt/bigIntConversion';
|
||||
|
||||
export default function longToBytes(sLong: string) {
|
||||
const bigIntBytes = bigIntToBytes(bigInt(sLong)).reverse();
|
||||
const bytes = addPadding(bigIntBytes, 8, true, false, false);
|
||||
// console.log('longToBytes', bytes, bigIntBytes);
|
||||
|
||||
return bytes;
|
||||
}
|
11
src/helpers/long/sortLongsArray.ts
Normal file
11
src/helpers/long/sortLongsArray.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import bigInt from "big-integer";
|
||||
|
||||
export default function sortLongsArray(arr: string[]) {
|
||||
return arr.map(long => {
|
||||
return bigInt(long);
|
||||
}).sort((a, b) => {
|
||||
return a.compare(b);
|
||||
}).map(bigInt => {
|
||||
return bigInt.toString(10);
|
||||
});
|
||||
}
|
@ -8,7 +8,7 @@ import MovableElement, { MovableElementOptions, MovableState } from "../componen
|
||||
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
|
||||
import ListenerSetter from "./listenerSetter";
|
||||
import mediaSizes, { ScreenSize } from "./mediaSizes";
|
||||
import { safeAssign } from "./object";
|
||||
import safeAssign from "./object/safeAssign";
|
||||
|
||||
export default class MovablePanel {
|
||||
#movable: MovableElement;
|
||||
|
4
src/helpers/number/intToUint.ts
Normal file
4
src/helpers/number/intToUint.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default function intToUint(val: number) {
|
||||
// return val < 0 ? val + 4294967296 : val; // 0 <= val <= Infinity
|
||||
return val >>> 0; // (4294967296 >>> 0) === 0; 0 <= val <= 4294967295
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*
|
||||
* Originally from:
|
||||
* https://github.com/zhukov/webogram
|
||||
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
|
||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export function copy<T>(obj: T): T {
|
||||
//in case of premitives
|
||||
if(obj === null || typeof(obj) !== "object") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
//date objects should be
|
||||
if(obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as any;
|
||||
}
|
||||
|
||||
//handle Array
|
||||
if(Array.isArray(obj)) {
|
||||
// @ts-ignore
|
||||
const clonedArr: T = obj.map(el => copy(el)) as any as T;
|
||||
return clonedArr;
|
||||
}
|
||||
|
||||
//lastly, handle objects
|
||||
// @ts-ignore
|
||||
let clonedObj = new obj.constructor();
|
||||
for(var prop in obj) {
|
||||
if(obj.hasOwnProperty(prop)) {
|
||||
clonedObj[prop] = copy(obj[prop]);
|
||||
}
|
||||
}
|
||||
return clonedObj;
|
||||
}
|
||||
|
||||
export function deepEqual(x: any, y: any): boolean {
|
||||
const ok = Object.keys, tx = typeof x, ty = typeof y;
|
||||
return x && y && tx === 'object' && tx === ty ? (
|
||||
ok(x).length === ok(y).length &&
|
||||
ok(x).every(key => deepEqual(x[key], y[key]))
|
||||
) : (x === y);
|
||||
}
|
||||
|
||||
export function defineNotNumerableProperties<T extends any>(obj: T, names: (keyof T)[]) {
|
||||
//const perf = performance.now();
|
||||
const props = {writable: true, configurable: true};
|
||||
const out: {[name in keyof T]?: typeof props} = {};
|
||||
names.forEach(name => {
|
||||
if(!obj.hasOwnProperty(name)) {
|
||||
out[name] = props;
|
||||
}
|
||||
});
|
||||
Object.defineProperties(obj, out);
|
||||
//console.log('defineNotNumerableProperties time:', performance.now() - perf);
|
||||
}
|
||||
|
||||
export function getObjectKeysAndSort(object: {[key: string]: any}, sort: 'asc' | 'desc' = 'asc') {
|
||||
if(!object) return [];
|
||||
const ids = object instanceof Map ? [...object.keys()] : Object.keys(object).map(i => +i);
|
||||
if(sort === 'asc') return ids.sort((a, b) => a - b);
|
||||
else return ids.sort((a, b) => b - a);
|
||||
}
|
||||
|
||||
export function safeReplaceObject(wasObject: any, newObject: any) {
|
||||
if(!wasObject) {
|
||||
return newObject;
|
||||
}
|
||||
|
||||
for(var key in wasObject) {
|
||||
if(!newObject.hasOwnProperty(key)) {
|
||||
delete wasObject[key];
|
||||
}
|
||||
}
|
||||
|
||||
for(var key in newObject) {
|
||||
//if (newObject.hasOwnProperty(key)) { // useless
|
||||
wasObject[key] = newObject[key];
|
||||
//}
|
||||
}
|
||||
|
||||
return wasObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be used for FILE_REFERENCE_EXPIRED
|
||||
* @param key
|
||||
* @param wasObject
|
||||
* @param newObject
|
||||
*/
|
||||
export function safeReplaceArrayInObject<K>(key: K, wasObject: any, newObject: any) {
|
||||
if('byteLength' in newObject[key]) { // Uint8Array
|
||||
newObject[key] = [...newObject[key]];
|
||||
}
|
||||
|
||||
if(wasObject && wasObject[key] !== newObject[key]) {
|
||||
wasObject[key].length = newObject[key].length;
|
||||
(newObject[key] as any[]).forEach((v, i) => {
|
||||
wasObject[key][i] = v;
|
||||
});
|
||||
|
||||
/* wasObject[key].set(newObject[key]); */
|
||||
newObject[key] = wasObject[key];
|
||||
}
|
||||
}
|
||||
|
||||
export function isObject<T extends Record<any, any>>(object: any): object is T {
|
||||
return typeof(object) === 'object' && object !== null;
|
||||
}
|
||||
|
||||
export function getDeepProperty(object: any, key: string) {
|
||||
const splitted = key.split('.');
|
||||
let o: any = object;
|
||||
splitted.forEach(key => {
|
||||
if(!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
o = o[key];
|
||||
});
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
export function setDeepProperty(object: any, key: string, value: any) {
|
||||
const splitted = key.split('.');
|
||||
getDeepProperty(object, splitted.slice(0, -1).join('.'))[splitted.pop()] = value;
|
||||
}
|
||||
|
||||
export function validateInitObject(initObject: any, currentObject: any, onReplace?: (key: string) => void, previousKey?: string) {
|
||||
for(const key in initObject) {
|
||||
if(typeof(currentObject[key]) !== typeof(initObject[key])) {
|
||||
currentObject[key] = copy(initObject[key]);
|
||||
onReplace && onReplace(previousKey || key);
|
||||
} else if(isObject(initObject[key])) {
|
||||
validateInitObject(initObject[key], currentObject[key], onReplace, previousKey || key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function safeAssign<T>(object: T, fromObject: any) {
|
||||
if(fromObject) {
|
||||
for(let i in fromObject) {
|
||||
if(fromObject[i] !== undefined) {
|
||||
// @ts-ignore
|
||||
object[i] = fromObject[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
28
src/helpers/object/copy.ts
Normal file
28
src/helpers/object/copy.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export default function copy<T>(obj: T): T {
|
||||
//in case of premitives
|
||||
if(obj === null || typeof(obj) !== "object") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
//date objects should be
|
||||
if(obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as any;
|
||||
}
|
||||
|
||||
//handle Array
|
||||
if(Array.isArray(obj)) {
|
||||
// @ts-ignore
|
||||
const clonedArr: T = obj.map(el => copy(el)) as any as T;
|
||||
return clonedArr;
|
||||
}
|
||||
|
||||
//lastly, handle objects
|
||||
// @ts-ignore
|
||||
let clonedObj = new obj.constructor();
|
||||
for(var prop in obj){
|
||||
if(obj.hasOwnProperty(prop)) {
|
||||
clonedObj[prop] = copy(obj[prop]);
|
||||
}
|
||||
}
|
||||
return clonedObj;
|
||||
}
|
7
src/helpers/object/deepEqual.ts
Normal file
7
src/helpers/object/deepEqual.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export default function deepEqual(x: any, y: any): boolean {
|
||||
const ok = Object.keys, tx = typeof x, ty = typeof y;
|
||||
return x && y && tx === 'object' && tx === ty ? (
|
||||
ok(x).length === ok(y).length &&
|
||||
ok(x).every(key => deepEqual(x[key], y[key]))
|
||||
) : (x === y);
|
||||
}
|
12
src/helpers/object/defineNotNumerableProperties.ts
Normal file
12
src/helpers/object/defineNotNumerableProperties.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export default function defineNotNumerableProperties<T extends any>(obj: T, names: (keyof T)[]) {
|
||||
//const perf = performance.now();
|
||||
const props = {writable: true, configurable: true};
|
||||
const out: {[name in keyof T]?: typeof props} = {};
|
||||
names.forEach(name => {
|
||||
if(!obj.hasOwnProperty(name)) {
|
||||
out[name] = props;
|
||||
}
|
||||
});
|
||||
Object.defineProperties(obj, out);
|
||||
//console.log('defineNotNumerableProperties time:', performance.now() - perf);
|
||||
}
|
14
src/helpers/object/getDeepProperty.ts
Normal file
14
src/helpers/object/getDeepProperty.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default function getDeepProperty(object: any, key: string) {
|
||||
const splitted = key.split('.');
|
||||
let o: any = object;
|
||||
splitted.forEach(key => {
|
||||
if(!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
o = o[key];
|
||||
});
|
||||
|
||||
return o;
|
||||
}
|
6
src/helpers/object/getObjectKeysAndSort.ts
Normal file
6
src/helpers/object/getObjectKeysAndSort.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export default function getObjectKeysAndSort(object: {[key: string]: any}, sort: 'asc' | 'desc' = 'asc') {
|
||||
if(!object) return [];
|
||||
const ids = object instanceof Map ? [...object.keys()] : Object.keys(object).map(i => +i);
|
||||
if(sort === 'asc') return ids.sort((a, b) => a - b);
|
||||
else return ids.sort((a, b) => b - a);
|
||||
}
|
3
src/helpers/object/isObject.ts
Normal file
3
src/helpers/object/isObject.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default function isObject<T extends Record<any, any>>(object: any): object is T {
|
||||
return typeof(object) === 'object' && object !== null;
|
||||
}
|
12
src/helpers/object/safeAssign.ts
Normal file
12
src/helpers/object/safeAssign.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export default function safeAssign<T>(object: T, fromObject: any) {
|
||||
if(fromObject) {
|
||||
for(let i in fromObject) {
|
||||
if(fromObject[i] !== undefined) {
|
||||
// @ts-ignore
|
||||
object[i] = fromObject[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
21
src/helpers/object/safeReplaceArrayInObject.ts
Normal file
21
src/helpers/object/safeReplaceArrayInObject.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Will be used for FILE_REFERENCE_EXPIRED
|
||||
* @param key
|
||||
* @param wasObject
|
||||
* @param newObject
|
||||
*/
|
||||
export default function safeReplaceArrayInObject<K>(key: K, wasObject: any, newObject: any) {
|
||||
if('byteLength' in newObject[key]) { // Uint8Array
|
||||
newObject[key] = [...newObject[key]];
|
||||
}
|
||||
|
||||
if(wasObject && wasObject[key] !== newObject[key]) {
|
||||
wasObject[key].length = newObject[key].length;
|
||||
(newObject[key] as any[]).forEach((v, i) => {
|
||||
wasObject[key][i] = v;
|
||||
});
|
||||
|
||||
/* wasObject[key].set(newObject[key]); */
|
||||
newObject[key] = wasObject[key];
|
||||
}
|
||||
}
|
19
src/helpers/object/safeReplaceObject.ts
Normal file
19
src/helpers/object/safeReplaceObject.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export default function safeReplaceObject(wasObject: any, newObject: any) {
|
||||
if(!wasObject) {
|
||||
return newObject;
|
||||
}
|
||||
|
||||
for(var key in wasObject) {
|
||||
if(!newObject.hasOwnProperty(key)) {
|
||||
delete wasObject[key];
|
||||
}
|
||||
}
|
||||
|
||||
for(var key in newObject) {
|
||||
//if (newObject.hasOwnProperty(key)) { // useless
|
||||
wasObject[key] = newObject[key];
|
||||
//}
|
||||
}
|
||||
|
||||
return wasObject;
|
||||
}
|
6
src/helpers/object/setDeepProperty.ts
Normal file
6
src/helpers/object/setDeepProperty.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import getDeepProperty from "./getDeepProperty";
|
||||
|
||||
export default function setDeepProperty(object: any, key: string, value: any) {
|
||||
const splitted = key.split('.');
|
||||
getDeepProperty(object, splitted.slice(0, -1).join('.'))[splitted.pop()] = value;
|
||||
}
|
13
src/helpers/object/validateInitObject.ts
Normal file
13
src/helpers/object/validateInitObject.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import copy from "./copy";
|
||||
import isObject from "./isObject";
|
||||
|
||||
export default function validateInitObject(initObject: any, currentObject: any, onReplace?: (key: string) => void, previousKey?: string) {
|
||||
for(const key in initObject) {
|
||||
if(typeof(currentObject[key]) !== typeof(initObject[key])) {
|
||||
currentObject[key] = copy(initObject[key]);
|
||||
onReplace && onReplace(previousKey || key);
|
||||
} else if(isObject(initObject[key])) {
|
||||
validateInitObject(initObject[key], currentObject[key], onReplace, previousKey || key);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import Scrollable from "../components/scrollable";
|
||||
import { safeAssign } from "./object";
|
||||
import safeAssign from "./object/safeAssign";
|
||||
|
||||
export default class ScrollableLoader {
|
||||
public loading = false;
|
||||
|
@ -10,7 +10,7 @@ import type { Message } from "../layer";
|
||||
import appMessagesIdsManager from "../lib/appManagers/appMessagesIdsManager";
|
||||
import appMessagesManager, { MyMessage } from "../lib/appManagers/appMessagesManager";
|
||||
import rootScope from "../lib/rootScope";
|
||||
import { forEachReverse } from "./array";
|
||||
import forEachReverse from "./array/forEachReverse";
|
||||
import filterChatPhotosMessages from "./filterChatPhotosMessages";
|
||||
import ListLoader, { ListLoaderOptions } from "./listLoader";
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import { insertInDescendSortedArray } from "./array";
|
||||
import insertInDescendSortedArray from "./array/insertInDescendSortedArray";
|
||||
import { getMiddleware } from "./middleware";
|
||||
import { safeAssign } from "./object";
|
||||
import safeAssign from "./object/safeAssign";
|
||||
|
||||
export type SortedElementId = PeerId;
|
||||
export type SortedElementBase = {
|
||||
|
13
src/helpers/toggleClassName.ts
Normal file
13
src/helpers/toggleClassName.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export default function toggleClassName(className: string, elements: HTMLElement[], disable: boolean) {
|
||||
elements.forEach((element) => {
|
||||
element.classList.toggle(className, disable);
|
||||
});
|
||||
|
||||
return () => toggleClassName(className, elements, !disable);
|
||||
}
|
@ -683,6 +683,8 @@ const lang = {
|
||||
"Devices": "Devices",
|
||||
"LanguageName": "English",
|
||||
"EditCantEditPermissionsPublic": "This permission is not available in public groups.",
|
||||
"VoipUserMicrophoneIsOff": "%s\'s microphone is off",
|
||||
"VoipUserCameraIsOff": "%s\'s camera is off",
|
||||
|
||||
// * macos
|
||||
"AccountSettings.Filters": "Chat Folders",
|
||||
|
@ -10,15 +10,338 @@
|
||||
*/
|
||||
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import IS_CALL_SUPPORTED from "../../environment/callSupport";
|
||||
import AudioAssetPlayer from "../../helpers/audioAssetPlayer";
|
||||
import bytesCmp from "../../helpers/bytes/bytesCmp";
|
||||
import safeReplaceObject from "../../helpers/object/safeReplaceObject";
|
||||
import { nextRandomUint } from "../../helpers/random";
|
||||
import tsNow from "../../helpers/tsNow";
|
||||
import { InputPhoneCall, MessagesDhConfig, PhoneCall, PhoneCallDiscardReason, PhoneCallProtocol, PhonePhoneCall } from "../../layer";
|
||||
import CallInstance from "../calls/callInstance";
|
||||
import CALL_STATE from "../calls/callState";
|
||||
import { logger } from "../logger";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import { NULL_PEER_ID } from "../mtproto/mtproto_config";
|
||||
import rootScope from "../rootScope";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appProfileManager from "./appProfileManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
||||
export type CallId = PhoneCall['id'];
|
||||
|
||||
export type MyPhoneCall = Exclude<PhoneCall, PhoneCall.phoneCallEmpty | PhoneCall.phoneCallDiscarded>;
|
||||
|
||||
const CALL_REQUEST_TIMEOUT = 45e3;
|
||||
|
||||
export type CallAudioAssetName = "call_busy.mp3" | "call_connect.mp3" | "call_end.mp3" | "call_incoming.mp3" | "call_outgoing.mp3" | "voip_failed.mp3" | "voip_connecting.mp3";
|
||||
|
||||
export class AppCallsManager {
|
||||
private log: ReturnType<typeof logger>;
|
||||
|
||||
private calls: Map<CallId, MyPhoneCall>;
|
||||
private instances: Map<CallId, CallInstance>;
|
||||
private tempId: number;
|
||||
private audioAsset: AudioAssetPlayer<CallAudioAssetName>;
|
||||
|
||||
constructor() {
|
||||
this.log = logger('CALLS');
|
||||
|
||||
|
||||
this.tempId = 0;
|
||||
this.calls = new Map();
|
||||
this.instances = new Map();
|
||||
|
||||
if(!IS_CALL_SUPPORTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootScope.addMultipleEventsListeners({
|
||||
updatePhoneCall: async(update) => {
|
||||
const call = this.saveCall(update.phone_call);
|
||||
|
||||
let instance = this.instances.get(call.id);
|
||||
|
||||
switch(call._) {
|
||||
case 'phoneCallDiscarded': {
|
||||
if(instance) {
|
||||
instance.hangUp(call.reason?._, true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'phoneCallAccepted': {
|
||||
if(instance) {
|
||||
instance.confirmCall();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'phoneCallRequested': {
|
||||
if(!instance) {
|
||||
instance = this.createCallInstance({
|
||||
isOutgoing: false,
|
||||
interlocutorUserId: call.admin_id
|
||||
});
|
||||
|
||||
instance.overrideConnectionState(CALL_STATE.PENDING);
|
||||
instance.setPhoneCall(call);
|
||||
instance.setHangUpTimeout(CALL_REQUEST_TIMEOUT, 'phoneCallDiscardReasonMissed');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'phoneCall': {
|
||||
if(!instance || instance.encryptionKey) {
|
||||
break;
|
||||
}
|
||||
|
||||
const g_a = instance.dh.g_a = call.g_a_or_b;
|
||||
const dh = instance.dh;
|
||||
const g_a_hash = await apiManager.invokeCrypto('sha256', g_a);
|
||||
if(!bytesCmp(dh.g_a_hash, g_a_hash)) {
|
||||
this.log.error('Incorrect g_a_hash', dh.g_a_hash, g_a_hash);
|
||||
break;
|
||||
}
|
||||
|
||||
const {key, key_fingerprint} = await this.computeKey(g_a, dh.b, dh.p);
|
||||
if(call.key_fingerprint !== key_fingerprint) {
|
||||
this.log.error('Incorrect key fingerprint', call.key_fingerprint, key_fingerprint);
|
||||
break;
|
||||
}
|
||||
|
||||
instance.encryptionKey = key;
|
||||
instance.joinCall();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updatePhoneCallSignalingData: (update) => {
|
||||
const instance = this.instances.get(update.phone_call_id);
|
||||
if(instance?.id !== update.phone_call_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance.onUpdatePhoneCallSignalingData(update);
|
||||
}
|
||||
});
|
||||
|
||||
this.audioAsset = new AudioAssetPlayer<CallAudioAssetName>([
|
||||
'call_busy.mp3',
|
||||
'call_connect.mp3',
|
||||
'call_end.mp3',
|
||||
'call_incoming.mp3',
|
||||
'call_outgoing.mp3',
|
||||
'voip_failed.mp3'
|
||||
]);
|
||||
}
|
||||
|
||||
public get currentCall() {
|
||||
let lastInstance: CallInstance;
|
||||
for(const [callId, instance] of this.instances) {
|
||||
lastInstance = instance;
|
||||
if(instance.connectionState !== CALL_STATE.PENDING) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastInstance;
|
||||
}
|
||||
|
||||
public getCallByUserId(userId: UserId) {
|
||||
for(const [callId, instance] of this.instances) {
|
||||
if(instance.interlocutorUserId === userId) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async computeKey(g_b: Uint8Array, a: Uint8Array, p: Uint8Array) {
|
||||
return apiManager.invokeCrypto('compute-dh-key', g_b, a, p);
|
||||
}
|
||||
|
||||
public saveCall(call: PhoneCall) {
|
||||
const isDiscarded = call._ === 'phoneCallDiscarded';
|
||||
const oldCall = this.calls.get(call.id);
|
||||
if(oldCall) {
|
||||
// if(shouldUpdate) {
|
||||
safeReplaceObject(oldCall, call);
|
||||
// }
|
||||
|
||||
if(isDiscarded) {
|
||||
this.calls.delete(call.id);
|
||||
}
|
||||
|
||||
call = oldCall;
|
||||
} else if(!isDiscarded) {
|
||||
this.calls.set(call.id, call as any);
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
public getCall(callId: CallId) {
|
||||
return this.calls.get(callId);
|
||||
}
|
||||
|
||||
public getCallInput(id: CallId): InputPhoneCall {
|
||||
const call = this.getCall(id);
|
||||
return {
|
||||
_: 'inputPhoneCall',
|
||||
id: call.id,
|
||||
access_hash: call.access_hash
|
||||
};
|
||||
}
|
||||
|
||||
private createCallInstance(options: {
|
||||
isOutgoing: boolean,
|
||||
interlocutorUserId: UserId,
|
||||
protocol?: PhoneCallProtocol
|
||||
}) {
|
||||
const call = new CallInstance({
|
||||
appCallsManager: this,
|
||||
apiManager,
|
||||
apiUpdatesManager,
|
||||
...options,
|
||||
});
|
||||
|
||||
let wasTryingToJoin = false;
|
||||
call.addEventListener('state', (state) => {
|
||||
const currentCall = this.currentCall;
|
||||
if(state === CALL_STATE.CLOSED) {
|
||||
this.instances.delete(call.id);
|
||||
}
|
||||
|
||||
if(state === CALL_STATE.EXCHANGING_KEYS) {
|
||||
wasTryingToJoin = true;
|
||||
}
|
||||
|
||||
const hasConnected = call.connectedAt !== undefined;
|
||||
if(state === CALL_STATE.EXCHANGING_KEYS || (state === CALL_STATE.CONNECTING && hasConnected)) {
|
||||
call.setHangUpTimeout(CALL_REQUEST_TIMEOUT, 'phoneCallDiscardReasonDisconnect');
|
||||
} else {
|
||||
call.clearHangUpTimeout();
|
||||
}
|
||||
|
||||
if(currentCall === call || !currentCall) {
|
||||
if(state === CALL_STATE.CLOSED) {
|
||||
if(!call.isOutgoing && !wasTryingToJoin) { // incoming call has been accepted on other device or ended
|
||||
this.audioAsset.stopSound();
|
||||
} else if(wasTryingToJoin && !hasConnected) { // something has happened during the key exchanging
|
||||
this.audioAsset.playSound('voip_failed.mp3');
|
||||
} else {
|
||||
this.audioAsset.playSound(call.discardReason === 'phoneCallDiscardReasonBusy' ? 'call_busy.mp3' : 'call_end.mp3');
|
||||
}
|
||||
} else if(state === CALL_STATE.PENDING) {
|
||||
this.audioAsset.playSound(call.isOutgoing ? 'call_outgoing.mp3' : 'call_incoming.mp3', true);
|
||||
} else if(state === CALL_STATE.EXCHANGING_KEYS) {
|
||||
this.audioAsset.playSoundIfDifferent('call_connect.mp3');
|
||||
} else if(state === CALL_STATE.CONNECTING) {
|
||||
if(call.duration) {
|
||||
this.audioAsset.playSound('voip_connecting.mp3', true);
|
||||
}
|
||||
} else {
|
||||
this.audioAsset.stopSound();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
call.addEventListener('id', (id, prevId) => {
|
||||
if(prevId !== undefined) {
|
||||
this.instances.delete(prevId);
|
||||
}
|
||||
|
||||
const hasCurrent = !!this.currentCall;
|
||||
this.instances.set(id, call);
|
||||
|
||||
if(prevId === undefined) {
|
||||
rootScope.dispatchEvent('call_instance', {instance: call, hasCurrent: hasCurrent});
|
||||
}
|
||||
});
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
public savePhonePhoneCall(phonePhoneCall: PhonePhoneCall) {
|
||||
appUsersManager.saveApiUsers(phonePhoneCall.users);
|
||||
return this.saveCall(phonePhoneCall.phone_call);
|
||||
}
|
||||
|
||||
public generateDh() {
|
||||
return apiManager.invokeApi('messages.getDhConfig', {
|
||||
version: 0,
|
||||
random_length: 256
|
||||
}).then(async(dhConfig) => {
|
||||
return apiManager.invokeCrypto('generate-dh', dhConfig as MessagesDhConfig.messagesDhConfig);
|
||||
});
|
||||
}
|
||||
|
||||
public startCallInternal(userId: UserId, isVideo: boolean) {
|
||||
this.log('p2pStartCallInternal', userId, isVideo);
|
||||
|
||||
const fullInfo = appProfileManager.getCachedFullUser(userId);
|
||||
if(!fullInfo) return;
|
||||
|
||||
const {video_calls_available} = fullInfo.pFlags;
|
||||
|
||||
const call = this.createCallInstance({
|
||||
isOutgoing: true,
|
||||
interlocutorUserId: userId
|
||||
});
|
||||
|
||||
call.requestInputSource(true, !!(isVideo && video_calls_available), false);
|
||||
|
||||
call.overrideConnectionState(CALL_STATE.REQUESTING);
|
||||
call.setPhoneCall({
|
||||
_: 'phoneCallWaiting',
|
||||
access_hash: '',
|
||||
admin_id: NULL_PEER_ID,
|
||||
date: tsNow(true),
|
||||
id: --this.tempId,
|
||||
participant_id: userId,
|
||||
protocol: call.protocol,
|
||||
pFlags: {
|
||||
video: isVideo || undefined
|
||||
}
|
||||
});
|
||||
|
||||
// return;
|
||||
this.generateDh().then(dh => {
|
||||
call.dh = dh;
|
||||
|
||||
return apiManager.invokeApi('phone.requestCall', {
|
||||
user_id: appUsersManager.getUserInput(userId),
|
||||
protocol: call.protocol,
|
||||
video: isVideo && video_calls_available,
|
||||
random_id: nextRandomUint(32),
|
||||
g_a_hash: call.dh.g_a_hash
|
||||
});
|
||||
}).then(result => {
|
||||
const phoneCall = this.savePhonePhoneCall(result);
|
||||
call.overrideConnectionState(CALL_STATE.PENDING);
|
||||
call.setPhoneCall(phoneCall);
|
||||
call.setHangUpTimeout(CALL_REQUEST_TIMEOUT, 'phoneCallDiscardReasonHangup');
|
||||
});
|
||||
}
|
||||
|
||||
public async discardCall(callId: CallId, duration: number, reason: PhoneCallDiscardReason['_'], video?: boolean) {
|
||||
if(!this.getCall(callId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updates = await apiManager.invokeApi('phone.discardCall', {
|
||||
video,
|
||||
peer: this.getCallInput(callId),
|
||||
duration,
|
||||
reason: {
|
||||
_: reason
|
||||
},
|
||||
connection_id: '0'
|
||||
});
|
||||
|
||||
apiUpdatesManager.processUpdateMessage(updates);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,11 @@
|
||||
*/
|
||||
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
|
||||
import { isRestricted } from "../../helpers/restrictions";
|
||||
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates, ChannelsCreateChannel, Peer } from "../../layer";
|
||||
import copy from "../../helpers/object/copy";
|
||||
import deepEqual from "../../helpers/object/deepEqual";
|
||||
import isObject from "../../helpers/object/isObject";
|
||||
import safeReplaceObject from "../../helpers/object/safeReplaceObject";
|
||||
import { ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates } from "../../layer";
|
||||
import apiManagerProxy from "../mtproto/mtprotoworker";
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
@ -21,6 +23,7 @@ import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appPeersManager from "./appPeersManager";
|
||||
import appStateManager from "./appStateManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import { isRestricted } from "../../helpers/restrictions";
|
||||
|
||||
export type Channel = Chat.channel;
|
||||
export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat' | 'view_participants';
|
||||
|
@ -10,7 +10,6 @@
|
||||
*/
|
||||
|
||||
import { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName';
|
||||
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object';
|
||||
import { Document, InputFileLocation, InputMedia, PhotoSize } from '../../layer';
|
||||
import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase';
|
||||
import opusDecodeController from '../opusDecodeController';
|
||||
@ -24,6 +23,9 @@ import { getFullDate } from '../../helpers/date';
|
||||
import rootScope from '../rootScope';
|
||||
import IS_WEBP_SUPPORTED from '../../environment/webpSupport';
|
||||
import IS_WEBM_SUPPORTED from '../../environment/webmSupport';
|
||||
import defineNotNumerableProperties from '../../helpers/object/defineNotNumerableProperties';
|
||||
import isObject from '../../helpers/object/isObject';
|
||||
import safeReplaceArrayInObject from '../../helpers/object/safeReplaceArrayInObject';
|
||||
|
||||
export type MyDocument = Document.document;
|
||||
|
||||
|
@ -18,12 +18,12 @@ import serverTimeManager from "../mtproto/serverTimeManager";
|
||||
import { MessageEntity, DraftMessage, MessagesSaveDraft } from "../../layer";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import { tsNow } from "../../helpers/date";
|
||||
import { deepEqual } from "../../helpers/object";
|
||||
import { isObject } from "../mtproto/bin_utils";
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import stateStorage from "../stateStorage";
|
||||
import appMessagesIdsManager from "./appMessagesIdsManager";
|
||||
import assumeType from "../../helpers/assumeType";
|
||||
import isObject from "../../helpers/object/isObject";
|
||||
import deepEqual from "../../helpers/object/deepEqual";
|
||||
|
||||
export type MyDraftMessage = DraftMessage.draftMessage;
|
||||
|
||||
|
@ -6,10 +6,10 @@
|
||||
|
||||
import App from "../../config/app";
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { indexOfAndSplice } from "../../helpers/array";
|
||||
import { validateInitObject } from "../../helpers/object";
|
||||
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
|
||||
import isObject from "../../helpers/object/isObject";
|
||||
import validateInitObject from "../../helpers/object/validateInitObject";
|
||||
import I18n from "../langPack";
|
||||
import { isObject } from "../mtproto/bin_utils";
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
import RichTextProcessor from "../richtextprocessor";
|
||||
import rootScope from "../rootScope";
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import AudioAssetPlayer from "../../helpers/audioAssetPlayer";
|
||||
import { safeReplaceObject } from "../../helpers/object";
|
||||
import safeReplaceObject from "../../helpers/object/safeReplaceObject";
|
||||
import { nextRandomUint } from "../../helpers/random";
|
||||
import tsNow from "../../helpers/tsNow";
|
||||
import { GroupCall, GroupCallParticipant, GroupCallParticipantVideo, GroupCallParticipantVideoSourceGroup, InputGroupCall, Peer, PhoneJoinGroupCall, PhoneJoinGroupCallPresentation, Update, Updates } from "../../layer";
|
||||
|
@ -45,7 +45,6 @@ import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search';
|
||||
import I18n, { i18n, join, LangPackKey } from '../langPack';
|
||||
import { ChatInvite, Dialog, Message, SendMessageAction } from '../../layer';
|
||||
import { hslaStringToHex } from '../../helpers/color';
|
||||
import { copy, getObjectKeysAndSort } from '../../helpers/object';
|
||||
import { getFilesFromEvent } from '../../helpers/files';
|
||||
import PeerTitle from '../../components/peerTitle';
|
||||
import PopupPeer from '../../components/popups/peer';
|
||||
@ -77,6 +76,7 @@ import TopbarCall from '../../components/topbarCall';
|
||||
import confirmationPopup from '../../components/confirmationPopup';
|
||||
import IS_GROUP_CALL_SUPPORTED from '../../environment/groupCallSupport';
|
||||
import appAvatarsManager from './appAvatarsManager';
|
||||
import appCallsManager from './appCallsManager';
|
||||
import IS_CALL_SUPPORTED from '../../environment/callSupport';
|
||||
import { CallType } from '../calls/types';
|
||||
import { Modify, SendMessageEmojiInteractionData } from '../../types';
|
||||
@ -84,6 +84,9 @@ import htmlToSpan from '../../helpers/dom/htmlToSpan';
|
||||
import getVisibleRect from '../../helpers/dom/getVisibleRect';
|
||||
import { simulateClickEvent } from '../../helpers/dom/clickEvent';
|
||||
import appReactionsManager from './appReactionsManager';
|
||||
import PopupCall from '../../components/call';
|
||||
import copy from '../../helpers/object/copy';
|
||||
import getObjectKeysAndSort from '../../helpers/object/getObjectKeysAndSort';
|
||||
|
||||
//console.log('appImManager included33!');
|
||||
|
||||
@ -341,7 +344,7 @@ export class AppImManager {
|
||||
this.topbarCall = new TopbarCall(appGroupCallsManager, appPeersManager, appChatsManager, appAvatarsManager);
|
||||
}
|
||||
|
||||
/* if(IS_CALL_SUPPORTED) {
|
||||
if(IS_CALL_SUPPORTED) {
|
||||
rootScope.addEventListener('call_instance', ({instance, hasCurrent}) => {
|
||||
if(hasCurrent) {
|
||||
return;
|
||||
@ -349,12 +352,11 @@ export class AppImManager {
|
||||
|
||||
new PopupCall({
|
||||
appAvatarsManager,
|
||||
appCallsManager,
|
||||
appPeersManager,
|
||||
instance
|
||||
}).show();
|
||||
});
|
||||
} */
|
||||
}
|
||||
|
||||
// ! do not remove this line
|
||||
// ! instance can be deactivated before the UI starts, because it waits in background for RAF that is delayed
|
||||
@ -918,7 +920,7 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
public async callUser(userId: UserId, type: CallType) {
|
||||
/* const call = appCallsManager.getCallByUserId(userId);
|
||||
const call = appCallsManager.getCallByUserId(userId);
|
||||
if(call) {
|
||||
return;
|
||||
}
|
||||
@ -939,17 +941,17 @@ export class AppImManager {
|
||||
|
||||
await this.discardCurrentCall(userId.toPeerId());
|
||||
|
||||
appCallsManager.startCallInternal(userId, type === 'video'); */
|
||||
appCallsManager.startCallInternal(userId, type === 'video');
|
||||
}
|
||||
|
||||
private discardCurrentCall(toPeerId: PeerId) {
|
||||
/* if(appCallsManager.currentCall) return this.discardCallConfirmation(toPeerId);
|
||||
if(appCallsManager.currentCall) return this.discardCallConfirmation(toPeerId);
|
||||
else if(appGroupCallsManager.groupCall) return this.discardGroupCallConfirmation(toPeerId);
|
||||
else return Promise.resolve(); */
|
||||
else return Promise.resolve();
|
||||
}
|
||||
|
||||
private async discardCallConfirmation(toPeerId: PeerId) {
|
||||
/* const currentCall = appCallsManager.currentCall;
|
||||
const currentCall = appCallsManager.currentCall;
|
||||
if(currentCall) {
|
||||
await confirmationPopup({
|
||||
titleLangKey: 'Call.Confirm.Discard.Call.Header',
|
||||
@ -966,7 +968,7 @@ export class AppImManager {
|
||||
if(appCallsManager.currentCall === currentCall) {
|
||||
await currentCall.hangUp();
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
private async discardGroupCallConfirmation(toPeerId: PeerId) {
|
||||
|
@ -22,8 +22,8 @@ import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import rootScope from "../rootScope";
|
||||
import appDraftsManager from "./appDraftsManager";
|
||||
import appMessagesIdsManager from "./appMessagesIdsManager";
|
||||
import { insertInDescendSortedArray } from "../../helpers/array";
|
||||
import appStateManager from "./appStateManager";
|
||||
import insertInDescendSortedArray from "../../helpers/array/insertInDescendSortedArray";
|
||||
|
||||
export class AppInlineBotsManager {
|
||||
private inlineResults: {[queryAndResultIds: string]: BotInlineResult} = {};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user