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.
 
 
 
 
 

288 lines
9.5 KiB

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import PopupGroupCall from '.';
import filterAsync from '../../helpers/array/filterAsync';
import contextMenuController from '../../helpers/contextMenuController';
import {attachContextMenuListener} from '../../helpers/dom/attachContextMenuListener';
import cancelEvent from '../../helpers/dom/cancelEvent';
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/safeAssign';
import positionMenu from '../../helpers/positionMenu';
import ScrollableLoader from '../../helpers/scrollableLoader';
import {GroupCallParticipant} from '../../layer';
import appImManager from '../../lib/appManagers/appImManager';
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 {ButtonMenuItemOptions, ButtonMenuSync} from '../buttonMenu';
import confirmationPopup from '../confirmationPopup';
import PeerTitle from '../peerTitle';
import PopupElement from '../popups';
import Scrollable from '../scrollable';
import GroupCallParticipantsList from './participantsList';
import GroupCallParticipantsVideoElement from './participantVideos';
export class GroupCallParticipantContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: (peerId: PeerId) => boolean | Promise<boolean>})[];
private element: HTMLElement;
private chatId: ChatId;
private targetPeerId: PeerId;
private participant: GroupCallParticipant;
private instance: GroupCallInstance;
private canManageCall: boolean;
private managers: AppManagers;
constructor(options: {
listenerSetter: ListenerSetter,
onContextElement: HTMLElement,
managers: AppManagers,
instance: GroupCallInstance,
}) {
this.buttons = [{
icon: 'gc_microphoneoff',
text: 'VoiceChat.MutePeer',
verify: () => this.canManageCall && this.participant.pFlags.can_self_unmute,
onClick: () => this.toggleParticipantMuted(true)
}, {
icon: 'gc_microphone',
text: 'VoiceChat.UnmutePeer',
verify: () => this.canManageCall && !this.participant.pFlags.can_self_unmute,
onClick: () => this.toggleParticipantMuted(false)
}, {
icon: 'gc_microphoneoff',
text: 'VoiceChat.MuteForMe',
verify: () => !this.canManageCall && !this.participant.pFlags.muted_by_you,
onClick: () => this.toggleParticipantMuted(true)
}, {
icon: 'gc_microphone',
text: 'VoiceChat.UnmuteForMe',
verify: () => !this.canManageCall && this.participant.pFlags.muted_by_you,
onClick: () => this.toggleParticipantMuted(false)
}, {
icon: 'newprivate',
text: 'VoiceChat.OpenProfile',
verify: () => true,
onClick: this.onOpenProfileClick
}, {
icon: 'deleteuser danger',
text: 'VoiceChat.RemovePeer',
verify: () => this.managers.appChatsManager.hasRights(this.chatId, 'ban_users'),
onClick: async() => {
confirmationPopup({
peerId: this.targetPeerId,
title: new PeerTitle({peerId: this.targetPeerId}).element,
descriptionLangKey: await this.managers.appChatsManager.isBroadcast(this.chatId) ? 'VoiceChat.RemovePeer.Confirm.Channel' : 'VoiceChat.RemovePeer.Confirm',
descriptionLangArgs: [new PeerTitle({peerId: this.targetPeerId}).element],
button: {
langKey: 'VoiceChat.RemovePeer.Confirm.OK',
isDanger: true
}
}).then(() => {
this.managers.appChatsManager.kickFromChat(this.chatId, this.targetPeerId);
}, noop);
}
}];
const {listenerSetter} = options;
this.managers = options.managers;
this.instance = options.instance;
this.chatId = this.instance.chatId;
this.element = ButtonMenuSync({buttons: this.buttons, listenerSetter});
this.element.classList.add('group-call-participant-menu', 'night');
attachContextMenuListener({
element: options.onContextElement,
callback: async(e) => {
const li = findUpClassName(e.target, 'group-call-participant');
if(!li) {
return;
}
if(this.element.parentElement !== appendTo) {
appendTo.append(this.element);
}
cancelEvent(e);
const peerId = this.targetPeerId = li.dataset.peerId.toPeerId();
this.participant = await this.instance.getParticipantByPeerId(peerId);
if(this.participant.pFlags.self) {
return;
}
this.canManageCall = await this.managers.appChatsManager.hasRights(this.chatId, 'manage_call');
await filterAsync(this.buttons, async(button) => {
const good = await button.verify(peerId);
button.element.classList.toggle('hide', !good);
return good;
});
positionMenu((e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent, this.element, 'right');
contextMenuController.openBtnMenu(this.element);
},
listenerSetter
});
listenerSetter.add(rootScope)('group_call_participant', ({groupCallId, participant}) => {
if(this.instance.id === groupCallId) {
const peerId = getPeerId(participant.peer);
if(this.targetPeerId === peerId) {
contextMenuController.close();
}
}
});
let appendTo: HTMLElement = document.body;
addFullScreenListener(document.body, () => {
const isFull = isFullScreen();
appendTo = isFull ? PopupElement.getPopups(PopupGroupCall)[0].getContainer(): document.body;
if(!isFull) {
contextMenuController.close();
}
}, listenerSetter);
}
private onOpenProfileClick = () => {
const popup = PopupElement.getPopups(PopupGroupCall)[0];
if(popup) {
popup.hide();
}
appImManager.setInnerPeer({peerId: this.targetPeerId});
};
private toggleParticipantMuted = (muted: boolean) => {
this.instance.editParticipant(this.participant, {
muted
});
};
};
export default class GroupCallParticipantsElement {
private container: HTMLDivElement;
private sortedList: GroupCallParticipantsList;
private instance: GroupCallInstance;
private listenerSetter: ListenerSetter;
private groupCallParticipantsVideo: GroupCallParticipantsVideoElement;
private contextMenu: GroupCallParticipantContextMenu;
private managers: AppManagers;
constructor(options: {
appendTo: HTMLElement,
instance: GroupCallInstance,
listenerSetter: ListenerSetter,
managers: AppManagers
}) {
safeAssign(this, options);
const className = 'group-call-participants';
const scrollable = new Scrollable(undefined);
scrollable.container.classList.add(className + '-scrollable');
const container = this.container = document.createElement('div');
container.classList.add(className);
// const invite = Button(`btn-primary btn-transparent ${className}-invite`, {icon: 'adduser', text: 'VoiceChat.Invite.InviteMembers'});
const sortedList = this.sortedList = new GroupCallParticipantsList(this.instance);
const {instance, listenerSetter} = this;
this.contextMenu = new GroupCallParticipantContextMenu({
...options,
onContextElement: sortedList.list,
listenerSetter,
instance
});
this.groupCallParticipantsVideo = new GroupCallParticipantsVideoElement({
...options,
appendTo: scrollable.container,
displayPinned: false
});
scrollable.append(/* invite, */sortedList.list);
container.append(scrollable.container);
options.appendTo.append(container);
listenerSetter.add(rootScope)('group_call_participant', ({groupCallId, participant}) => {
if(this.instance.id === groupCallId) {
this.updateParticipant(participant);
}
});
const scrollableLoader = new ScrollableLoader({
scrollable,
getPromise: () => {
return this.managers.appGroupCallsManager.getGroupCallParticipants(this.instance.id).then(({participants, isEnd}) => {
participants.forEach((participant) => {
this.updateParticipant(participant);
});
return isEnd;
});
}
});
this.setInstance(instance);
}
private updateParticipant(participant: GroupCallParticipant) {
const peerId = getPeerId(participant.peer);
const has = this.sortedList.has(peerId);
if(participant.pFlags.left) {
if(has) {
this.sortedList.delete(peerId);
}
return;
}
if(!has) {
this.sortedList.add(peerId);
return;
}
this.sortedList.update(peerId);
}
public async setInstance(instance: GroupCallInstance) {
// @ts-ignore
/* const users = appUsersManager.users;
for(const userId in users) {
const participant: GroupCallParticipant = {
_: 'groupCallParticipant',
date: 0,
peer: {_: 'peerUser', user_id: userId.toPeerId()},
pFlags: {
muted: true
},
source: 1
};
instance.participants.set(userId.toPeerId(), participant);
this.updateParticipant(participant);
} */
const participants = await instance.participants;
participants.forEach((participant) => {
this.updateParticipant(participant);
});
}
public destroy() {
this.sortedList.destroy();
this.groupCallParticipantsVideo.destroy();
}
}