ca1213c32f
Fix chat date blinking Fix displaying sent messages to new dialog Scroll to date bubble if message is bigger than viewport Fix releasing keyboard by inline helper Fix clearing self user Fix displaying sent public poll Update contacts counter in dialogs placeholder Improve multiselect animation Disable lottie icon animations if they're disabled Fix changing mtproto transport during authorization
178 lines
5.9 KiB
TypeScript
178 lines
5.9 KiB
TypeScript
/*
|
|
* https://github.com/morethanwords/tweb
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
*/
|
|
|
|
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 { GroupCallParticipant } from "../../layer";
|
|
import { AppGroupCallsManager, GroupCallOutputSource } from "../../lib/appManagers/appGroupCallsManager";
|
|
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
|
|
import GroupCallInstance from "../../lib/calls/groupCallInstance";
|
|
import rootScope from "../../lib/rootScope";
|
|
import GroupCallParticipantVideoElement, { GroupCallParticipantVideoType } from "./participantVideo";
|
|
|
|
export default class GroupCallParticipantsVideoElement extends ControlsHover {
|
|
private container: HTMLDivElement;
|
|
private instance: GroupCallInstance;
|
|
private appGroupCallsManager: AppGroupCallsManager;
|
|
private appPeersManager: AppPeersManager;
|
|
private participantsElements: Map<PeerId, Map<GroupCallParticipantVideoType, GroupCallParticipantVideoElement>>;
|
|
private displayPinned: boolean;
|
|
private containers: Map<HTMLElement, GroupCallParticipantVideoElement>;
|
|
private onLengthChange: (length: number) => void;
|
|
|
|
constructor(options: {
|
|
appendTo: HTMLElement,
|
|
appGroupCallsManager: AppGroupCallsManager,
|
|
appPeersManager: AppPeersManager,
|
|
instance: GroupCallInstance,
|
|
listenerSetter: ListenerSetter,
|
|
displayPinned: boolean,
|
|
onLengthChange?: GroupCallParticipantsVideoElement['onLengthChange']
|
|
}) {
|
|
super();
|
|
safeAssign(this, options);
|
|
|
|
const className = 'group-call-participants-video';
|
|
const container = this.container = document.createElement('div');
|
|
this.container.classList.add(className + '-container');
|
|
|
|
options.appendTo.append(container);
|
|
|
|
this.participantsElements = new Map();
|
|
this.containers = new Map();
|
|
|
|
const {listenerSetter} = this;
|
|
|
|
listenerSetter.add(rootScope)('group_call_participant', ({groupCallId, participant}) => {
|
|
if(this.instance.id === groupCallId) {
|
|
this.updateParticipant(participant);
|
|
}
|
|
});
|
|
|
|
listenerSetter.add(this.instance)('pinned', (source) => {
|
|
this.participantsElements.forEach((map) => {
|
|
map.forEach((element) => {
|
|
this.setElementDisplay(element, source);
|
|
});
|
|
});
|
|
});
|
|
|
|
attachClickEvent(this.container, (e) => {
|
|
const container = findUpClassName(e.target, 'group-call-participant-video-container');
|
|
if(!container) {
|
|
return;
|
|
}
|
|
|
|
const element = this.containers.get(container);
|
|
if(this.instance.pinnedSource === element.source) {
|
|
this.instance.unpinAll();
|
|
return;
|
|
}
|
|
|
|
this.instance.pinSource(element.source);
|
|
}, {listenerSetter});
|
|
|
|
this.setInstance(this.instance);
|
|
|
|
this.setup({
|
|
element: container,
|
|
listenerSetter: listenerSetter,
|
|
showOnLeaveToClassName: 'group-call-buttons'
|
|
});
|
|
}
|
|
|
|
private shouldDisplayElement(element: GroupCallParticipantVideoElement, pinnedSource: GroupCallOutputSource) {
|
|
return this.displayPinned ? !pinnedSource || element.source === pinnedSource : pinnedSource && element.source !== pinnedSource;
|
|
}
|
|
|
|
private setElementDisplay(element: GroupCallParticipantVideoElement, pinnedSource: GroupCallOutputSource) {
|
|
const shouldDisplay = this.shouldDisplayElement(element, pinnedSource);
|
|
element.container.classList.toggle('video-hidden', !shouldDisplay);
|
|
|
|
const isPinned = element.source === pinnedSource;
|
|
element.setPinned(isPinned);
|
|
}
|
|
|
|
private updateParticipant(participant: GroupCallParticipant) {
|
|
const peerId = this.appPeersManager.getPeerId(participant.peer);
|
|
const types: GroupCallParticipantVideoType[] = ['video', 'presentation'];
|
|
const hasAnyVideo = types.some(type => !!participant[type]);
|
|
let participantElements = this.participantsElements.get(peerId);
|
|
if(!hasAnyVideo && !participantElements) {
|
|
return;
|
|
}
|
|
|
|
if(!participantElements) {
|
|
this.participantsElements.set(peerId, participantElements = new Map());
|
|
}
|
|
|
|
types.forEach(type => {
|
|
let element = participantElements.get(type);
|
|
const participantVideo = participant[type];
|
|
if(!!participantVideo === !!element) {
|
|
if(element) {
|
|
element.updateParticipant(participant);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if(participantVideo) {
|
|
const result = this.instance.getVideoElementFromParticipantByType(participant, type);
|
|
if(!result) {
|
|
return;
|
|
}
|
|
|
|
const {video, source} = result;
|
|
|
|
element = new GroupCallParticipantVideoElement(this.appPeersManager, this.instance, source);
|
|
|
|
this.containers.set(element.container, element);
|
|
|
|
this.setElementDisplay(element, this.instance.pinnedSource);
|
|
participantElements.set(type, element);
|
|
element.setParticipant(participant, type, video);
|
|
|
|
this.container.prepend(element.container);
|
|
} else {
|
|
participantElements.delete(type);
|
|
element.container.remove();
|
|
|
|
if(!participantElements.size) {
|
|
this.participantsElements.delete(peerId);
|
|
this.containers.delete(element.container);
|
|
element.destroy();
|
|
}
|
|
}
|
|
|
|
this._onLengthChange();
|
|
});
|
|
}
|
|
|
|
private _onLengthChange() {
|
|
const length = this.container.childElementCount;
|
|
this.container.dataset.length = '' + length;
|
|
this.container.dataset.layout = length <= 2 ? '1' : (length === 3 ? '3' : '4');
|
|
|
|
this.onLengthChange && this.onLengthChange(length);
|
|
}
|
|
|
|
public setInstance(instance: GroupCallInstance) {
|
|
instance.participants.forEach((participant) => {
|
|
this.updateParticipant(participant);
|
|
});
|
|
}
|
|
|
|
public destroy() {
|
|
this.containers.forEach((element) => {
|
|
element.destroy();
|
|
});
|
|
}
|
|
}
|