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.
 
 
 
 
 

287 lines
9.7 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 type { AppChatsManager } from "../../lib/appManagers/appChatsManager";
import type { AppGroupCallsManager } from "../../lib/appManagers/appGroupCallsManager";
import appImManager from "../../lib/appManagers/appImManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
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 ButtonMenu, { ButtonMenuItemOptions } 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: HTMLDivElement;
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 = ButtonMenu(this.buttons, listenerSetter);
this.element.classList.add('group-call-participant-menu', 'night');
attachContextMenuListener(options.onContextElement, async(e: any) => {
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.closeBtnMenu();
}
}
});
let appendTo: HTMLElement = document.body;
addFullScreenListener(document.body, () => {
const isFull = isFullScreen();
appendTo = isFull ? PopupElement.getPopups(PopupGroupCall)[0].getContainer(): document.body;
if(!isFull) {
contextMenuController.closeBtnMenu();
}
}, 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();
}
}