Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.8 KiB
176 lines
5.8 KiB
/* |
|
* 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/safeAssign'; |
|
import {GroupCallParticipant} from '../../layer'; |
|
import {GroupCallOutputSource} from '../../lib/appManagers/appGroupCallsManager'; |
|
import {AppManagers} from '../../lib/appManagers/managers'; |
|
import getPeerId from '../../lib/appManagers/utils/peers/getPeerId'; |
|
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 participantsElements: Map<PeerId, Map<GroupCallParticipantVideoType, GroupCallParticipantVideoElement>>; |
|
private displayPinned: boolean; |
|
private containers: Map<HTMLElement, GroupCallParticipantVideoElement>; |
|
private onLengthChange: (length: number) => void; |
|
private managers: AppManagers; |
|
|
|
constructor(options: { |
|
appendTo: HTMLElement, |
|
instance: GroupCallInstance, |
|
listenerSetter: ListenerSetter, |
|
displayPinned: boolean, |
|
onLengthChange?: GroupCallParticipantsVideoElement['onLengthChange'], |
|
managers: AppManagers |
|
}) { |
|
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 = 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.managers, 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 async setInstance(instance: GroupCallInstance) { |
|
(await instance.participants).forEach((participant) => { |
|
this.updateParticipant(participant); |
|
}); |
|
} |
|
|
|
public destroy() { |
|
this.containers.forEach((element) => { |
|
element.destroy(); |
|
}); |
|
} |
|
}
|
|
|