|
|
|
/*
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
|
|
|
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
|
|
|
import ListenerSetter from "../helpers/listenerSetter";
|
|
|
|
import type { AppGroupCallsManager } from "../lib/appManagers/appGroupCallsManager";
|
|
|
|
import GROUP_CALL_STATE from "../lib/calls/groupCallState";
|
|
|
|
import rootScope from "../lib/rootScope";
|
|
|
|
import ButtonIcon from "./buttonIcon";
|
|
|
|
import TopbarWeave from "./topbarWeave";
|
|
|
|
import SetTransition from "./singleTransition";
|
|
|
|
import PopupGroupCall from "./groupCall";
|
|
|
|
import type { AppPeersManager } from "../lib/appManagers/appPeersManager";
|
|
|
|
import type { AppChatsManager } from "../lib/appManagers/appChatsManager";
|
|
|
|
import GroupCallDescriptionElement from "./groupCall/description";
|
|
|
|
import GroupCallTitleElement from "./groupCall/title";
|
|
|
|
import PopupElement from "./popups";
|
|
|
|
import throttle from "../helpers/schedulers/throttle";
|
|
|
|
import GroupCallInstance from "../lib/calls/groupCallInstance";
|
|
|
|
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 type { AppAvatarsManager } from "../lib/appManagers/appAvatarsManager";
|
|
|
|
import GroupCallMicrophoneIconMini from "./groupCall/microphoneIconMini";
|
|
|
|
import CallInstance from "../lib/calls/callInstance";
|
|
|
|
import type { AppCallsManager } from "../lib/appManagers/appCallsManager";
|
|
|
|
|
|
|
|
function convertCallStateToGroupState(state: CALL_STATE, isMuted: boolean) {
|
|
|
|
switch(state) {
|
|
|
|
case CALL_STATE.CLOSING:
|
|
|
|
case CALL_STATE.CLOSED:
|
|
|
|
return GROUP_CALL_STATE.CLOSED;
|
|
|
|
case CALL_STATE.CONNECTED:
|
|
|
|
return isMuted ? GROUP_CALL_STATE.MUTED : GROUP_CALL_STATE.UNMUTED;
|
|
|
|
default:
|
|
|
|
return GROUP_CALL_STATE.CONNECTING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const CLASS_NAME = 'topbar-call';
|
|
|
|
|
|
|
|
export default class TopbarCall {
|
|
|
|
public container: HTMLElement;
|
|
|
|
private listenerSetter: ListenerSetter;
|
|
|
|
private weave: TopbarWeave;
|
|
|
|
private center: HTMLDivElement;
|
|
|
|
private groupCallTitle: GroupCallTitleElement;
|
|
|
|
private groupCallDescription: GroupCallDescriptionElement;
|
|
|
|
private groupCallMicrophoneIconMini: GroupCallMicrophoneIconMini;
|
|
|
|
private callDescription: CallDescriptionElement;
|
|
|
|
|
|
|
|
private currentDescription: GroupCallDescriptionElement | CallDescriptionElement;
|
|
|
|
|
|
|
|
private instance: GroupCallInstance | any/* CallInstance */;
|
|
|
|
private instanceListenerSetter: ListenerSetter;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
private appGroupCallsManager: AppGroupCallsManager,
|
|
|
|
private appPeersManager: AppPeersManager,
|
|
|
|
private appChatsManager: AppChatsManager,
|
|
|
|
private appAvatarsManager: AppAvatarsManager,
|
|
|
|
private appCallsManager: AppCallsManager
|
|
|
|
) {
|
|
|
|
const listenerSetter = this.listenerSetter = new ListenerSetter();
|
|
|
|
|
|
|
|
listenerSetter.add(rootScope)('call_instance', ({instance}) => {
|
|
|
|
if(!this.instance) {
|
|
|
|
this.updateInstance(instance);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
listenerSetter.add(rootScope)('call_accepting', (instance) => {
|
|
|
|
if(this.instance !== instance) {
|
|
|
|
this.updateInstance(instance);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
listenerSetter.add(rootScope)('group_call_instance', (instance) => {
|
|
|
|
this.updateInstance(instance);
|
|
|
|
});
|
|
|
|
|
|
|
|
listenerSetter.add(rootScope)('group_call_update', (groupCall) => {
|
|
|
|
const instance = this.appGroupCallsManager.groupCall;
|
|
|
|
if(instance?.id === groupCall.id) {
|
|
|
|
this.updateInstance(instance);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
listenerSetter.add(rootScope)('group_call_amplitude', ({amplitudes, type}) => {
|
|
|
|
const {weave} = this;
|
|
|
|
if(!amplitudes.length || !weave/* || type !== 'input' */) return;
|
|
|
|
|
|
|
|
let max = 0;
|
|
|
|
for(let i = 0; i < amplitudes.length; ++i) {
|
|
|
|
const {type, value} = amplitudes[i];
|
|
|
|
max = value > max ? value : max;
|
|
|
|
}
|
|
|
|
|
|
|
|
weave.setAmplitude(max);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private onState = () => {
|
|
|
|
this.updateInstance(this.instance);
|
|
|
|
};
|
|
|
|
|
|
|
|
private clearCurrentInstance() {
|
|
|
|
if(!this.instance) return;
|
|
|
|
this.center.textContent = '';
|
|
|
|
|
|
|
|
if(this.currentDescription) {
|
|
|
|
this.currentDescription.detach();
|
|
|
|
this.currentDescription = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.instance = undefined;
|
|
|
|
this.instanceListenerSetter.removeAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
private updateInstance(instance: TopbarCall['instance']) {
|
|
|
|
if(this.construct) {
|
|
|
|
this.construct();
|
|
|
|
this.construct = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const isChangingInstance = this.instance !== instance;
|
|
|
|
if(isChangingInstance) {
|
|
|
|
this.clearCurrentInstance();
|
|
|
|
|
|
|
|
this.instance = instance;
|
|
|
|
this.instanceListenerSetter = new ListenerSetter();
|
|
|
|
|
|
|
|
this.instanceListenerSetter.add(instance as GroupCallInstance)('state', this.onState);
|
|
|
|
|
|
|
|
if(instance instanceof GroupCallInstance) {
|
|
|
|
this.currentDescription = this.groupCallDescription;
|
|
|
|
} else {
|
|
|
|
this.currentDescription = this.callDescription;
|
|
|
|
this.instanceListenerSetter.add(instance)('muted', this.onState);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.container.classList.toggle('is-call', !(instance instanceof GroupCallInstance));
|
|
|
|
}
|
|
|
|
|
|
|
|
const isMuted = this.instance.isMuted;
|
|
|
|
let state = instance instanceof GroupCallInstance ? instance.state : convertCallStateToGroupState(instance.connectionState, isMuted);
|
|
|
|
|
|
|
|
const {weave} = this;
|
|
|
|
|
|
|
|
weave.componentDidMount();
|
|
|
|
|
|
|
|
const isClosed = state === GROUP_CALL_STATE.CLOSED;
|
|
|
|
if((!document.body.classList.contains('is-calling') || isChangingInstance) || isClosed) {
|
|
|
|
if(isClosed) {
|
|
|
|
weave.setAmplitude(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
SetTransition(document.body, 'is-calling', !isClosed, 250, isClosed ? () => {
|
|
|
|
weave.componentWillUnmount();
|
|
|
|
|
|
|
|
this.clearCurrentInstance();
|
|
|
|
}: undefined);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(isClosed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
weave.setCurrentState(state, true);
|
|
|
|
// if(state === GROUP_CALL_STATE.CONNECTING) {
|
|
|
|
// weave.setCurrentState(state, true);
|
|
|
|
// } else {
|
|
|
|
// /* var a = 0;
|
|
|
|
// animate(() => {
|
|
|
|
// a += 0.1;
|
|
|
|
// if(a > 1) a = 0;
|
|
|
|
// weave.setAmplitude(a);
|
|
|
|
// return true;
|
|
|
|
// });
|
|
|
|
// weave.setAmplitude(1); */
|
|
|
|
// weave.setCurrentState(state, true);
|
|
|
|
// }
|
|
|
|
|
|
|
|
this.setTitle(instance);
|
|
|
|
this.setDescription(instance);
|
|
|
|
this.groupCallMicrophoneIconMini.setState(!isMuted);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setDescription(instance: TopbarCall['instance']) {
|
|
|
|
return this.currentDescription.update(instance as any);
|
|
|
|
}
|
|
|
|
|
|
|
|
private setTitle(instance: TopbarCall['instance']) {
|
|
|
|
if(instance instanceof GroupCallInstance) {
|
|
|
|
return this.groupCallTitle.update(instance);
|
|
|
|
} else {
|
|
|
|
replaceContent(this.center, new PeerTitle({peerId: instance.interlocutorUserId.toPeerId()}).element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private construct() {
|
|
|
|
const {listenerSetter} = this;
|
|
|
|
const container = this.container = document.createElement('div');
|
|
|
|
container.classList.add('sidebar-header', CLASS_NAME + '-container');
|
|
|
|
|
|
|
|
const left = document.createElement('div');
|
|
|
|
left.classList.add(CLASS_NAME + '-left');
|
|
|
|
|
|
|
|
const groupCallMicrophoneIconMini = this.groupCallMicrophoneIconMini = new GroupCallMicrophoneIconMini();
|
|
|
|
|
|
|
|
const mute = ButtonIcon();
|
|
|
|
mute.append(groupCallMicrophoneIconMini.container);
|
|
|
|
left.append(mute);
|
|
|
|
|
|
|
|
const throttledMuteClick = throttle(() => {
|
|
|
|
this.instance.toggleMuted();
|
|
|
|
}, 600, true);
|
|
|
|
|
|
|
|
attachClickEvent(mute, (e) => {
|
|
|
|
cancelEvent(e);
|
|
|
|
throttledMuteClick();
|
|
|
|
}, {listenerSetter});
|
|
|
|
|
|
|
|
const center = this.center = document.createElement('div');
|
|
|
|
center.classList.add(CLASS_NAME + '-center');
|
|
|
|
|
|
|
|
this.groupCallTitle = new GroupCallTitleElement(center);
|
|
|
|
this.groupCallDescription = new GroupCallDescriptionElement(left);
|
|
|
|
|
|
|
|
this.callDescription = new CallDescriptionElement(left);
|
|
|
|
|
|
|
|
const right = document.createElement('div');
|
|
|
|
right.classList.add(CLASS_NAME + '-right');
|
|
|
|
|
|
|
|
const end = ButtonIcon('endcall_filled');
|
|
|
|
right.append(end);
|
|
|
|
|
|
|
|
attachClickEvent(end, (e) => {
|
|
|
|
cancelEvent(e);
|
|
|
|
|
|
|
|
const {instance} = this;
|
|
|
|
if(!instance) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(instance instanceof GroupCallInstance) {
|
|
|
|
instance.hangUp();
|
|
|
|
} else {
|
|
|
|
instance.hangUp('phoneCallDiscardReasonHangup');
|
|
|
|
}
|
|
|
|
}, {listenerSetter});
|
|
|
|
|
|
|
|
attachClickEvent(container, () => {
|
|
|
|
if(this.instance instanceof GroupCallInstance) {
|
|
|
|
if(PopupElement.getPopup(PopupGroupCall)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new PopupGroupCall({
|
|
|
|
appGroupCallsManager: this.appGroupCallsManager,
|
|
|
|
appPeersManager: this.appPeersManager,
|
|
|
|
appChatsManager: this.appChatsManager
|
|
|
|
}).show();
|
|
|
|
} else if(this.instance instanceof CallInstance) {
|
|
|
|
const hasPopup = PopupElement.getPopup(PopupCall) as PopupCall;
|
|
|
|
if(hasPopup && hasPopup.getCallInstance() === this.instance) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new PopupCall({
|
|
|
|
appCallsManager: this.appCallsManager,
|
|
|
|
appAvatarsManager: this.appAvatarsManager,
|
|
|
|
appPeersManager: this.appPeersManager,
|
|
|
|
instance: this.instance
|
|
|
|
}).show();
|
|
|
|
}
|
|
|
|
}, {listenerSetter});
|
|
|
|
|
|
|
|
container.append(left, center, right);
|
|
|
|
|
|
|
|
const weave = this.weave = new TopbarWeave();
|
|
|
|
const weaveContainer = weave.render(CLASS_NAME + '-weave');
|
|
|
|
container.prepend(weaveContainer);
|
|
|
|
|
|
|
|
document.getElementById('column-center').prepend(container);
|
|
|
|
weave.componentDidMount();
|
|
|
|
}
|
|
|
|
}
|