Video chat fixes
This commit is contained in:
parent
2186c66d27
commit
cc7c5e772e
@ -33,9 +33,9 @@ import { IS_APPLE_MOBILE } from "../../environment/userAgent";
|
||||
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
|
||||
import toggleDisability from "../../helpers/dom/toggleDisability";
|
||||
import { ripple } from "../ripple";
|
||||
import debounce from "../../helpers/schedulers/debounce";
|
||||
import throttle from "../../helpers/schedulers/throttle";
|
||||
import IS_SCREEN_SHARING_SUPPORTED from "../../environment/screenSharingSupport";
|
||||
import ListenerSetter from "../../helpers/listenerSetter";
|
||||
|
||||
export enum GROUP_CALL_PARTICIPANT_MUTED_STATE {
|
||||
UNMUTED,
|
||||
@ -116,6 +116,34 @@ let previousState: MovableState = {
|
||||
height: 640
|
||||
};
|
||||
|
||||
const className = 'group-call';
|
||||
|
||||
function makeButton(listenerSetter: ListenerSetter, options: {
|
||||
text?: LangPackKey,
|
||||
isDanger?: boolean,
|
||||
noRipple?: boolean,
|
||||
callback?: () => void,
|
||||
listenerSetter?: ListenerSetter
|
||||
}) {
|
||||
const _className = className + '-button';
|
||||
const div = document.createElement('div');
|
||||
div.classList.add(_className, 'rp-overflow');
|
||||
|
||||
if(!options.noRipple) {
|
||||
ripple(div);
|
||||
}
|
||||
|
||||
if(options.isDanger) {
|
||||
div.classList.add(_className + '-red');
|
||||
}
|
||||
|
||||
if(options.callback) {
|
||||
attachClickEvent(div, options.callback, {listenerSetter: options.listenerSetter});
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
export default class PopupGroupCall extends PopupElement {
|
||||
private appGroupCallsManager: AppGroupCallsManager;
|
||||
private appPeersManager: AppPeersManager;
|
||||
@ -135,6 +163,8 @@ export default class PopupGroupCall extends PopupElement {
|
||||
private movable: MovableElement;
|
||||
private buttonsContainer: HTMLDivElement;
|
||||
private btnFullScreen2: HTMLButtonElement;
|
||||
private btnVideo: HTMLDivElement;
|
||||
private btnScreen: HTMLDivElement;
|
||||
|
||||
constructor(options: {
|
||||
appGroupCallsManager: AppGroupCallsManager,
|
||||
@ -150,19 +180,18 @@ export default class PopupGroupCall extends PopupElement {
|
||||
safeAssign(this, options);
|
||||
|
||||
this.videosCount = 0;
|
||||
this.container.classList.add('group-call', 'night');
|
||||
this.container.classList.add(className, 'night');
|
||||
|
||||
const instance = this.instance = this.appGroupCallsManager.groupCall;
|
||||
const {listenerSetter} = this;
|
||||
|
||||
if(!IS_APPLE_MOBILE) {
|
||||
const btnFullScreen = this.btnFullScreen = ButtonIcon('fullscreen');
|
||||
const btnFullScreen2 = this.btnFullScreen2 = ButtonIcon('fullscreen group-call-cfs');
|
||||
const btnFullScreen2 = this.btnFullScreen2 = ButtonIcon('fullscreen ' + className + '-cfs');
|
||||
const btnExitFullScreen = this.btnExitFullScreen = ButtonIcon('smallscreen');
|
||||
|
||||
attachClickEvent(btnFullScreen, () => {
|
||||
requestFullScreen(this.container);
|
||||
}, {listenerSetter});
|
||||
attachClickEvent(btnFullScreen, this.onFullScreenClick, {listenerSetter});
|
||||
attachClickEvent(btnFullScreen2, this.onFullScreenClick, {listenerSetter});
|
||||
|
||||
attachClickEvent(btnExitFullScreen, () => {
|
||||
cancelFullScreen();
|
||||
@ -172,22 +201,22 @@ export default class PopupGroupCall extends PopupElement {
|
||||
}
|
||||
|
||||
const btnInvite = this.btnInvite = ButtonIcon('adduser');
|
||||
const btnShowColumn = this.btnShowColumn = ButtonIcon('rightpanel group-call-only-big');
|
||||
const btnShowColumn = this.btnShowColumn = ButtonIcon('rightpanel ' + className + '-only-big');
|
||||
this.toggleMovable(!IS_TOUCH_SUPPORTED);
|
||||
|
||||
attachClickEvent(btnShowColumn, this.toggleRightColumn, {listenerSetter});
|
||||
|
||||
const headerInfo = document.createElement('div');
|
||||
headerInfo.classList.add('group-call-header-info');
|
||||
headerInfo.classList.add(className + '-header-info');
|
||||
|
||||
this.title.classList.add('group-call-header-title');
|
||||
this.title.classList.add(className + '-header-title');
|
||||
|
||||
const subtitle = document.createElement('div');
|
||||
subtitle.classList.add('group-call-header-subtitle');
|
||||
subtitle.classList.add(className + '-header-subtitle');
|
||||
|
||||
headerInfo.append(this.title, subtitle);
|
||||
|
||||
this.header.classList.add('group-call-header');
|
||||
this.header.classList.add(className + '-header');
|
||||
this.header.append(...[this.btnExitFullScreen, headerInfo/* , btnInvite */, this.btnFullScreen, btnShowColumn].filter(Boolean));
|
||||
|
||||
const newHeader = this.header.cloneNode(false) as HTMLElement;
|
||||
@ -210,6 +239,8 @@ export default class PopupGroupCall extends PopupElement {
|
||||
this.groupCallTitle = new GroupCallTitleElement(this.title);
|
||||
this.groupCallDescription = new GroupCallDescriptionElement(subtitle);
|
||||
this.groupCallBodyHeaderDescription = new GroupCallDescriptionElement(newHeaderTitle);
|
||||
this.constructButtons();
|
||||
|
||||
this.groupCallParticipantsVideo = new GroupCallParticipantsVideoElement({
|
||||
appendTo: videosScrollable.container,
|
||||
instance,
|
||||
@ -228,127 +259,6 @@ export default class PopupGroupCall extends PopupElement {
|
||||
...options
|
||||
});
|
||||
|
||||
const className = 'group-call';
|
||||
|
||||
{
|
||||
const buttons = this.buttonsContainer = document.createElement('div');
|
||||
buttons.classList.add(className + '-buttons');
|
||||
|
||||
this.listenerSetter.add(this.groupCallParticipantsVideo)('toggleControls', (show) => {
|
||||
this.container.classList.toggle('show-controls', show);
|
||||
buttons.classList.toggle('show-controls', show);
|
||||
});
|
||||
|
||||
const b = (options: {
|
||||
text?: LangPackKey,
|
||||
isDanger?: boolean,
|
||||
noRipple?: boolean
|
||||
}) => {
|
||||
const _className = className + '-button';
|
||||
const div = document.createElement('div');
|
||||
div.classList.add(_className, 'rp-overflow');
|
||||
|
||||
if(!options.noRipple) {
|
||||
ripple(div);
|
||||
}
|
||||
|
||||
if(options.isDanger) {
|
||||
div.classList.add(_className + '-red');
|
||||
}
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
const btnVideo = b({
|
||||
text: 'VoiceChat.Video.Stream.Video'
|
||||
});
|
||||
|
||||
btnVideo.classList.add('tgico-videocamera_filled');
|
||||
|
||||
attachClickEvent(btnVideo, () => {
|
||||
const toggle = toggleDisability([btnVideo], true);
|
||||
this.instance.toggleVideoSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
}, {listenerSetter});
|
||||
|
||||
const btnScreen = b({
|
||||
text: 'VoiceChat.Video.Stream.Screencast'
|
||||
});
|
||||
|
||||
btnScreen.classList.add('tgico-sharescreen_filled');
|
||||
btnScreen.classList.toggle('hide', !IS_SCREEN_SHARING_SUPPORTED);
|
||||
|
||||
attachClickEvent(btnScreen, () => {
|
||||
const toggle = toggleDisability([btnScreen], true);
|
||||
this.instance.toggleScreenSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
}, {listenerSetter});
|
||||
|
||||
const btnMute = b({noRipple: true});
|
||||
btnMute.classList.add('group-call-microphone-button');
|
||||
|
||||
const microphoneIcon = this.groupCallMicrophoneIcon = new GroupCallMicrophoneIcon();
|
||||
btnMute.append(microphoneIcon.container);
|
||||
|
||||
const throttledMuteClick = throttle(() => {
|
||||
const participant = this.instance.participant;
|
||||
if(!participant.pFlags.can_self_unmute) {
|
||||
if(participant.raise_hand_rating === undefined) {
|
||||
this.instance.changeRaiseHand(true);
|
||||
}
|
||||
} else {
|
||||
this.instance.toggleMuted();
|
||||
}
|
||||
}, 600, true);
|
||||
|
||||
attachClickEvent(btnMute, throttledMuteClick, {listenerSetter});
|
||||
|
||||
const btnMore = b({
|
||||
text: 'VoiceChat.Video.Stream.More'
|
||||
});
|
||||
|
||||
btnMore.classList.add('tgico-settings_filled', 'btn-disabled');
|
||||
btnMore.classList.toggle('hide', !IS_SCREEN_SHARING_SUPPORTED);
|
||||
|
||||
const btnLeave = b({
|
||||
text: 'VoiceChat.Leave',
|
||||
isDanger: true
|
||||
});
|
||||
|
||||
btnLeave.classList.add('tgico-close');
|
||||
|
||||
attachClickEvent(btnLeave, () => {
|
||||
const hangUp = (discard: boolean) => {
|
||||
this.instance.hangUp(discard);
|
||||
};
|
||||
|
||||
if(this.appChatsManager.hasRights(this.instance.chatId, 'manage_call')) {
|
||||
new PopupPeer('popup-end-video-chat', {
|
||||
titleLangKey: 'VoiceChat.End.Title',
|
||||
descriptionLangKey: 'VoiceChat.End.Text',
|
||||
checkboxes: [{
|
||||
text: 'VoiceChat.End.Third'
|
||||
}],
|
||||
buttons: [{
|
||||
langKey: 'VoiceChat.End.OK',
|
||||
callback: (checkboxes) => {
|
||||
hangUp(!!checkboxes.size);
|
||||
},
|
||||
isDanger: true,
|
||||
}]
|
||||
}).show();
|
||||
} else {
|
||||
hangUp(false);
|
||||
}
|
||||
}, {listenerSetter});
|
||||
|
||||
buttons.append(btnVideo, btnScreen, btnMute, btnMore, btnLeave);
|
||||
|
||||
this.container.append(buttons);
|
||||
}
|
||||
|
||||
listenerSetter.add(rootScope)('group_call_state', (instance) => {
|
||||
if(this.instance === instance) {
|
||||
this.updateInstance();
|
||||
@ -367,6 +277,8 @@ export default class PopupGroupCall extends PopupElement {
|
||||
}
|
||||
});
|
||||
|
||||
listenerSetter.add(this.groupCallParticipantsVideo)('toggleControls', this.onToggleControls);
|
||||
|
||||
listenerSetter.add(mediaSizes)('changeScreen', (from, to) => {
|
||||
if(to === ScreenSize.mobile || from === ScreenSize.mobile) {
|
||||
this.toggleMovable(!IS_TOUCH_SUPPORTED);
|
||||
@ -394,6 +306,115 @@ export default class PopupGroupCall extends PopupElement {
|
||||
this.updateInstance();
|
||||
}
|
||||
|
||||
private constructButtons() {
|
||||
const buttons = this.buttonsContainer = document.createElement('div');
|
||||
buttons.classList.add(className + '-buttons');
|
||||
|
||||
const _makeButton = makeButton.bind(null, this.listenerSetter);
|
||||
|
||||
const btnVideo = this.btnVideo = _makeButton({
|
||||
text: 'VoiceChat.Video.Stream.Video',
|
||||
callback: this.onVideoClick
|
||||
});
|
||||
|
||||
btnVideo.classList.add('tgico-videocamera_filled');
|
||||
|
||||
const btnScreen = this.btnScreen = _makeButton({
|
||||
text: 'VoiceChat.Video.Stream.Screencast',
|
||||
callback: this.onScreenClick
|
||||
});
|
||||
|
||||
btnScreen.classList.add('tgico-sharescreen_filled');
|
||||
btnScreen.classList.toggle('hide', !IS_SCREEN_SHARING_SUPPORTED);
|
||||
|
||||
const btnMute = _makeButton({
|
||||
noRipple: true,
|
||||
callback: throttle(this.onMuteClick, 600, true)
|
||||
});
|
||||
btnMute.classList.add(className + '-microphone-button');
|
||||
|
||||
const microphoneIcon = this.groupCallMicrophoneIcon = new GroupCallMicrophoneIcon();
|
||||
btnMute.append(microphoneIcon.container);
|
||||
|
||||
const btnMore = _makeButton({
|
||||
text: 'VoiceChat.Video.Stream.More'
|
||||
});
|
||||
|
||||
btnMore.classList.add('tgico-settings_filled', 'btn-disabled');
|
||||
btnMore.classList.toggle('hide', !IS_SCREEN_SHARING_SUPPORTED);
|
||||
|
||||
const btnLeave = _makeButton({
|
||||
text: 'VoiceChat.Leave',
|
||||
isDanger: true,
|
||||
callback: this.onLeaveClick
|
||||
});
|
||||
|
||||
btnLeave.classList.add('tgico-close');
|
||||
|
||||
buttons.append(btnVideo, btnScreen, btnMute, btnMore, btnLeave);
|
||||
|
||||
this.container.append(buttons);
|
||||
}
|
||||
|
||||
private onFullScreenClick = () => {
|
||||
requestFullScreen(this.container);
|
||||
};
|
||||
|
||||
private onToggleControls = (show: boolean) => {
|
||||
this.container.classList.toggle('show-controls', show);
|
||||
this.buttonsContainer.classList.toggle('show-controls', show);
|
||||
};
|
||||
|
||||
private onVideoClick = () => {
|
||||
const toggle = toggleDisability([this.btnVideo], true);
|
||||
this.instance.toggleVideoSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
};
|
||||
|
||||
private onScreenClick = () => {
|
||||
const toggle = toggleDisability([this.btnScreen], true);
|
||||
this.instance.toggleScreenSharing().finally(() => {
|
||||
toggle();
|
||||
});
|
||||
};
|
||||
|
||||
private onMuteClick = () => {
|
||||
const participant = this.instance.participant;
|
||||
if(!participant.pFlags.can_self_unmute) {
|
||||
if(participant.raise_hand_rating === undefined) {
|
||||
this.instance.changeRaiseHand(true);
|
||||
}
|
||||
} else {
|
||||
this.instance.toggleMuted();
|
||||
}
|
||||
};
|
||||
|
||||
private onLeaveClick = () => {
|
||||
const hangUp = (discard: boolean) => {
|
||||
this.instance.hangUp(discard);
|
||||
};
|
||||
|
||||
if(this.appChatsManager.hasRights(this.instance.chatId, 'manage_call')) {
|
||||
new PopupPeer('popup-end-video-chat', {
|
||||
titleLangKey: 'VoiceChat.End.Title',
|
||||
descriptionLangKey: 'VoiceChat.End.Text',
|
||||
checkboxes: [{
|
||||
text: 'VoiceChat.End.Third'
|
||||
}],
|
||||
buttons: [{
|
||||
langKey: 'VoiceChat.End.OK',
|
||||
callback: (checkboxes) => {
|
||||
hangUp(!!checkboxes.size);
|
||||
},
|
||||
isDanger: true,
|
||||
}]
|
||||
}).show();
|
||||
} else {
|
||||
hangUp(false);
|
||||
}
|
||||
};
|
||||
|
||||
public getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import constraintSupported, { MyMediaTrackSupportedConstraints } from "../../env
|
||||
import { IS_SAFARI } from "../../environment/userAgent";
|
||||
import { forEachReverse, indexOfAndSplice } from "../../helpers/array";
|
||||
import simulateEvent from "../../helpers/dom/dispatchEvent";
|
||||
import noop from "../../helpers/noop";
|
||||
import { safeAssign, safeReplaceObject } from "../../helpers/object";
|
||||
import { nextRandomUint } from "../../helpers/random";
|
||||
import throttle from "../../helpers/schedulers/throttle";
|
||||
@ -268,7 +269,7 @@ export class GroupCallInstance {
|
||||
private hadAutoPinnedSources: Set<GroupCallOutputSource>;
|
||||
private dispatchPinnedThrottled: () => void;
|
||||
private startVideoSharingPromise: Promise<void>;
|
||||
startScreenSharingPromise: Promise<void>;
|
||||
private startScreenSharingPromise: Promise<void>;
|
||||
|
||||
constructor(options: {
|
||||
id: GroupCallInstance['id'],
|
||||
@ -303,6 +304,11 @@ export class GroupCallInstance {
|
||||
this.dispatchPinnedThrottled = throttle(() => {
|
||||
rootScope.dispatchEvent('group_call_pinned', {instance: this, source: this.pinnedSource});
|
||||
}, 0, false);
|
||||
|
||||
// possible Safari fix
|
||||
const audio = new Audio();
|
||||
audio.play().catch(noop);
|
||||
this.elements.set('audio', audio);
|
||||
}
|
||||
|
||||
get connectionState() {
|
||||
|
@ -181,10 +181,12 @@
|
||||
&-icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
margin-top: 2px;
|
||||
// margin-top: 2px;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-size: 1.125rem;
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
}
|
||||
|
||||
&-container {
|
||||
@ -228,7 +230,9 @@
|
||||
|
||||
.group-call-participant-status-icon {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 0;
|
||||
// margin-top: 0;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,9 +286,7 @@
|
||||
.group-call-participant-status-container {
|
||||
opacity: .6;
|
||||
// margin-top: -1px; // if no icon
|
||||
|
||||
margin-top: -1px; // if with icon
|
||||
margin-bottom: -1px; // if with icon
|
||||
}
|
||||
|
||||
&-left {
|
||||
|
Loading…
Reference in New Issue
Block a user