Skip to content
Snippets Groups Projects
Unverified Commit 69eab929 authored by Guilherme Gazzo's avatar Guilherme Gazzo Committed by GitHub
Browse files

Chore: Remove unused Blaze templates after message rewrite (#28025)

parent d89e7f5a
No related branches found
No related tags found
No related merge requests found
Showing
with 19 additions and 897 deletions
import { hasAllPermission, hasAtLeastOnePermission, hasPermission, userHasAllPermission } from './hasPermission'; import { hasAllPermission, hasAtLeastOnePermission, hasPermission, userHasAllPermission } from './hasPermission';
import { hasRole, hasAnyRole } from './hasRole'; import { hasRole, hasAnyRole } from './hasRole';
import { AuthorizationUtils } from '../lib/AuthorizationUtils'; import { AuthorizationUtils } from '../lib/AuthorizationUtils';
import './requiresPermission.html';
import './startup'; import './startup';
export { hasAllPermission, hasAtLeastOnePermission, hasRole, hasAnyRole, hasPermission, userHasAllPermission, AuthorizationUtils }; export { hasAllPermission, hasAtLeastOnePermission, hasRole, hasAnyRole, hasPermission, userHasAllPermission, AuthorizationUtils };
<template name="requiresPermission">
{{#if hasPermission this}}
{{> Template.contentBlock}}
{{else}}
{{#if Template.elseBlock}}
{{> Template.elseBlock}}
{{else}}
{{_ "Not_found_or_not_allowed"}}
{{/if}}
{{/if}}
</template>
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
<label class="rc-input__label"> <label class="rc-input__label">
<div class="rc-input__wrapper"> <div class="rc-input__wrapper">
<div class="rc-input__icon"> <div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="magnifier" }} <svg class="rc-icon rc-input__icon-svg rc-input__icon-svg--magnifier" aria-hidden="true">
<use xlink:href="{{baseUrl}}#icon-magnifier"></use>
</svg>
</div> </div>
<input name="name" type="text" class="rc-input__element js-emojipicker-search" placeholder="{{_ "Search"}}" autofocus autocomplete="off"> <input name="name" type="text" class="rc-input__element js-emojipicker-search" placeholder="{{_ "Search"}}" autofocus autocomplete="off">
</div> </div>
......
...@@ -9,6 +9,7 @@ import { t } from '../../utils/client'; ...@@ -9,6 +9,7 @@ import { t } from '../../utils/client';
import { EmojiPicker } from './lib/EmojiPicker'; import { EmojiPicker } from './lib/EmojiPicker';
import { emoji } from '../lib/rocketchat'; import { emoji } from '../lib/rocketchat';
import './emojiPicker.html'; import './emojiPicker.html';
import { baseURI } from '../../../client/lib/baseURI';
const ESCAPE = 27; const ESCAPE = 27;
...@@ -22,6 +23,20 @@ const loadMoreLink = () => `<li class="emoji-picker-load-more"><a href="#load-mo ...@@ -22,6 +23,20 @@ const loadMoreLink = () => `<li class="emoji-picker-load-more"><a href="#load-mo
let customItems = 90; let customItems = 90;
const baseUrlFix = () => `${baseURI}${FlowRouter.current().path.substring(1)}`;
const isMozillaFirefoxBelowVersion = (upperVersion) => {
const [, version] = navigator.userAgent.match(/Firefox\/(\d+)\.\d/) || [];
return parseInt(version, 10) < upperVersion;
};
const isGoogleChromeBelowVersion = (upperVersion) => {
const [, version] = navigator.userAgent.match(/Chrome\/(\d+)\.\d/) || [];
return parseInt(version, 10) < upperVersion;
};
const isBaseUrlFixNeeded = () => isMozillaFirefoxBelowVersion(55) || isGoogleChromeBelowVersion(55);
const createEmojiList = (category, actualTone, limit = null) => { const createEmojiList = (category, actualTone, limit = null) => {
const html = const html =
Object.values(emoji.packages) Object.values(emoji.packages)
...@@ -167,6 +182,7 @@ Template.emojiPicker.helpers({ ...@@ -167,6 +182,7 @@ Template.emojiPicker.helpers({
currentCategory() { currentCategory() {
return EmojiPicker.currentCategory.get(); return EmojiPicker.currentCategory.get();
}, },
baseUrl: isBaseUrlFixNeeded() ? baseUrlFix : undefined,
}); });
Template.emojiPicker.events({ Template.emojiPicker.events({
......
import './lazyloadImage';
const map = new WeakMap();
const featureExists = !!window.IntersectionObserver;
const loadImage = (el) => {
const instance = map.get(el);
map.delete(el);
if (!instance) {
return;
}
const img = new Image();
const src = el.getAttribute('data-src');
img.onload = () => {
el.className = el.className.replace('lazy-img', '');
el.src = src;
el.removeAttribute('data-src');
instance.loaded.set(true);
};
img.src = src;
};
const observer =
featureExists &&
new IntersectionObserver(
(entries, observer) =>
entries.forEach((entry) => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
return loadImage(entry.target);
}
}),
{
threshold: [0],
trackVisibility: true,
delay: 230,
},
);
export const addImage = (instance) => {
const el = instance.firstNode;
map.set(el, instance);
if (featureExists) {
return observer.observe(el);
}
loadImage(el);
};
<template name="lazyloadImage">
<img data-src="{{srcUrl}}" src="{{lazySrcUrl}}" alt="{{alt}}" height="{{height}}" class="{{class}}"
data-title="{{title}}" data-description="{{description}}">
</template>
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import './lazyloadImage.html';
import { addImage } from '.';
const emptyImageEncoded = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+/u3PQAJJAM0dIyWdgAAAABJRU5ErkJggg==';
const imgsrcs = new Set();
Template.lazyloadImage.helpers({
class() {
const loaded = Template.instance().loaded.get();
return `${this.class} ${loaded ? '' : 'lazy-img'}`;
},
srcUrl() {
if (Template.instance().loaded.get()) {
return;
}
return this.src;
},
lazySrcUrl() {
const { preview, placeholder, src } = this;
const { loaded } = Template.instance();
if (loaded.get() || (!preview && !placeholder) || imgsrcs.has(src)) {
return src;
}
imgsrcs.add(this.src);
return `data:image/png;base64,${preview || emptyImageEncoded}`;
},
});
Template.lazyloadImage.onCreated(function () {
this.loaded = new ReactiveVar(false);
});
Template.lazyloadImage.onRendered(function () {
addImage(this);
});
export * from './client/index';
.npm/
import './views/liveStreamTab.html';
import './views/liveStreamTab';
import './views/livestreamBroadcast.html';
import './views/livestreamBroadcast';
import './views/broadcastView.html';
import './views/broadcastView';
import './views/liveStreamView.html';
import './views/liveStreamView';
import './tabBar';
import './styles/liveStreamTab.css';
import { Meteor } from 'meteor/meteor';
import { settings } from '../../settings';
export const close = (popup) =>
new Promise(function (resolve) {
const checkInterval = setInterval(() => {
if (popup.closed) {
clearInterval(checkInterval);
resolve();
}
}, 300);
});
export const auth = async () => {
const oauthWindow = window.open(
`${settings.get('Site_Url')}/api/v1/livestream/oauth?userId=${Meteor.userId()}`,
'youtube-integration-oauth',
'width=400,height=600',
);
return close(oauthWindow);
};
.contextual-bar__content {
&.livestream {
justify-content: flex-start;
}
& .rc-button--stack {
width: 100%;
}
}
.thumbnail-container {
position: relative;
height: 270px;
& .popout {
&--absolute-center {
position: absolute;
top: calc(50% - 25px);
left: calc(50% - 25px);
font-size: 50px;
}
&--play-solid {
fill: white;
}
}
}
.liveStreamTab__form {
& .rc-switch {
padding: 15px 5px;
}
& .rc-input__label p {
padding: 10px 0;
}
}
.livestream--url {
overflow: hidden;
margin: 10px 5px;
padding: 10px 0;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 15px;
& .current-setting {
display: inline-block;
text-decoration: none;
color: black;
}
& .rc-button {
width: 49%;
}
}
import type { ReactNode } from 'react';
import React, { useMemo } from 'react';
import { Option, Badge } from '@rocket.chat/fuselage';
import { useSetting, useTranslation } from '@rocket.chat/ui-contexts';
import { isRoomFederated } from '@rocket.chat/core-typings';
import { Header } from '@rocket.chat/ui-client';
import { addAction } from '../../../client/views/room/lib/Toolbox';
addAction('livestream', ({ room }) => {
const enabled = useSetting('Livestream_enabled');
const t = useTranslation();
const federated = isRoomFederated(room);
const isLive = room?.streamingOptions?.id && room.streamingOptions.type === 'livestream';
return useMemo(
() =>
enabled
? {
groups: ['channel', 'group', 'team'],
id: 'livestream',
title: 'Livestream',
icon: 'podcast',
template: 'liveStreamTab',
order: isLive ? -1 : 15,
...(federated && {
'data-tooltip': federated ? 'Livestream_unavailable_for_federation' : '',
'disabled': true,
}),
renderAction: (props): ReactNode => (
<Header.ToolBox.Action {...props}>
{isLive ? (
<Header.ToolBox.ActionBadge title={t('Livestream_live_now')} variant='danger'>
!
</Header.ToolBox.ActionBadge>
) : null}
</Header.ToolBox.Action>
),
renderOption: ({ label: { title, icon }, ...props }: any): ReactNode => (
<Option label={title} title={title} icon={icon} {...props}>
{isLive ? (
<Badge title={t('Livestream_live_now')} variant='danger'>
!
</Badge>
) : null}
</Option>
),
}
: null,
[enabled, isLive, t, federated],
);
});
<template name="broadcastView">
<div class="streaming-popup">
<video id="player" src={{ broadcastSource }} muted autoplay width="380px" ></video>
</div>
</template>
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';
import { dispatchToastMessage } from '../../../../client/lib/toast';
import { settings } from '../../../settings/client';
const getMedia = () => navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
const createAndConnect = (url) => {
if (!('WebSocket' in window)) {
// eslint-disable-line no-negated-in-lhs
return false;
}
const ws = new WebSocket(url);
ws.onerror = (evt) => console.error(`Error: ${evt.data}`);
return ws;
};
const sendMessageToWebSocket = (message, ws) => {
if (ws != null) {
if (ws.readyState === 1) {
ws.send(message);
}
}
};
export const call = (...args) =>
new Promise(function (resolve, reject) {
Meteor.call(...args, function (error, result) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
reject(error);
return;
}
resolve(result);
});
});
const delay = (time) => new Promise((resolve) => setTimeout(resolve, time));
const waitForStreamStatus = async (id, status) => {
const streamActive = new Promise(async (resolve) => {
// eslint-disable-next-line no-constant-condition
while (true) {
const currentStatus = await call('livestreamStreamStatus', { streamId: id }); // eslint-disable-line no-await-in-loop
if (currentStatus === status) {
return resolve(status);
}
await delay(1500); // eslint-disable-line no-await-in-loop
}
});
await streamActive;
};
const waitForBroadcastStatus = async (id, status) => {
const broadcastActive = new Promise(async (resolve) => {
// eslint-disable-next-line no-constant-condition
while (true) {
const currentStatus = await call('getBroadcastStatus', { broadcastId: id }); // eslint-disable-line no-await-in-loop
if (currentStatus === status) {
return resolve(status);
}
await delay(1500); // eslint-disable-line no-await-in-loop
}
});
await broadcastActive;
};
Template.broadcastView.helpers({
broadcastSource() {
return Template.instance().mediaStream.get() ? window.URL.createObjectURL(Template.instance().mediaStream.get()) : '';
},
mediaRecorder() {
Template.instance().mediaRecorder.get();
},
});
Template.broadcastView.onCreated(async function () {
const connection = createAndConnect(`${settings.get('Broadcasting_media_server_url')}/${this.data.id}`);
this.mediaStream = new ReactiveVar(null);
this.mediaRecorder = new ReactiveVar(null);
this.connection = new ReactiveVar(connection);
});
Template.broadcastView.onDestroyed(function () {
if (this.connection.get()) {
this.connection.get().close();
}
if (this.mediaRecorder.get()) {
this.mediaRecorder.get().stop();
this.mediaRecorder.set(null);
}
if (this.mediaStream.get()) {
this.mediaStream
.get()
.getTracks()
.map((track) => track.stop());
this.mediaStream.set(null);
}
});
Template.broadcastView.onRendered(async function () {
navigator.getMedia = getMedia();
if (!navigator.getMedia) {
return alert('getUserMedia() is not supported in your browser!');
}
const localMediaStream = await new Promise((resolve, reject) => navigator.getMedia({ video: true, audio: true }, resolve, reject));
const connection = this.connection.get();
this.mediaStream.set(localMediaStream);
let options = { mimeType: 'video/webm;codecs=vp9' };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
options = { mimeType: 'video/webm;codecs=vp8' };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
options = { mimeType: 'video/webm' };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
options = { mimeType: '' };
}
}
}
try {
const mediaRecorder = new MediaRecorder(localMediaStream, options);
mediaRecorder.ondataavailable = (event) => {
if (!(event.data || event.data.size > 0)) {
return;
}
sendMessageToWebSocket(event.data, connection);
};
mediaRecorder.start(100); // collect 100ms of data
this.mediaRecorder.set(mediaRecorder);
await waitForStreamStatus(this.data.stream.id, 'active');
await call('setLivestreamStatus', { broadcastId: this.data.broadcast.id, status: 'testing' });
await waitForBroadcastStatus(this.data.broadcast.id, 'testing');
document.querySelector('.streaming-popup').dispatchEvent(new Event('broadcastStreamReady'));
} catch (e) {
console.log(e);
}
});
Template.broadcastView.events({
async 'startStreaming .streaming-popup'(e, i) {
await call('setLivestreamStatus', { broadcastId: i.data.broadcast.id, status: 'live' });
document.querySelector('.streaming-popup').dispatchEvent(new Event('broadcastStream'));
await call('saveRoomSettings', Session.get('openedRoom'), 'streamingOptions', {
id: i.data.broadcast.id,
url: `https://www.youtube.com/embed/${i.data.broadcast.id}`,
thumbnail: `https://img.youtube.com/vi/${i.data.broadcast.id}/0.jpg`,
});
},
async 'stopStreaming .streaming-popup'(e, i) {
await call('setBroadcastStatus', { broadcastId: i.data.broadcast.id, status: 'complete' });
await call('saveRoomSettings', Session.get('openedRoom'), 'streamingOptions', {}, (error) => {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
i.editing.set(false);
i.streamingOptions.set({});
});
if (i.mediaRecorder.get()) {
i.mediaRecorder.get().stop();
i.mediaRecorder.set(null);
}
if (i.mediaStream.get()) {
i.mediaStream
.get()
.getTracks()
.map((track) => track.stop());
i.mediaStream.set(null);
}
},
});
<template name="liveStreamTab">
{{#if hasSource}}
{{#if isPopoutOpen}}
{{#if canDock}}
<button type="button" class="rc-button rc-button--primary js-close button-block" title="{{_ 'Livestream_close'}}" >{{_ "Livestream_close"}}</button>
{{else}}
<button type="button" class="rc-button rc-button--primary js-close button-block" title="{{_ 'Livestream_switch_to_room'}}" >{{_ "Livestream_switch_to_room"}}</button>
{{/if}}
{{else}}
{{#if hasThumbnail}}
<div class="thumbnail-container" style="background: url({{thumbnailUrl}}) 100% no-repeat no-repeat;">
<a href="#" class="js-popout popout--absolute-center">
{{> icon icon="play-solid" block="popout"}}
</a>
</div>
{{else}}
<a href="#" class="js-popout popout--absolute-center">
{{> icon icon="play-solid" block="popout"}}
</a>
{{/if}}
{{/if}}
{{else}}
<p> {{ streamingUnavailableMessage }} </p>
{{/if}}
{{#if canEdit}}
{{#if editing}}
{{#if broadcastEnabled}}
{{ > liveStreamBroadcast}}
{{/if}}
<form class="liveStreamTab__form">
<label class="rc-input__label">
<p> Livestream not available message: </p>
<input type="text" name="streaming-message" class="rc-input__element content-background-color editing" placeholder="{{_ "Livestream_not_found"}}" value="{{ streamingUnavailableMessage }}"/>
</label>
<label class="rc-input__label">
<p> Livestream source (URL): </p>
<input type="text" name="streaming-source" class="rc-input__element content-background-color editing" placeholder="{{_ "Livestream_url"}}" value="{{ streamingSource }}"/>
</label>
<div class="rc-switch">
<label class="rc-switch__label" tabindex="-1">
<input type="checkbox" class="rc-switch__input" name="streaming-audio-only" checked={{isAudioOnly}}>
<span class="rc-switch__button">
<span class="rc-switch__button-inside"></span>
</span>
<span class="rc-switch__text">
{{_ "Livestream_enable_audio_only"}}
</span>
</label>
<span class="rc-switch__description">{{ typeDescription }}</span>
</div>
<div class="rc-user-info__flex">
<button type="button" class="rc-button rc-button--nude js-cancel button-block" title="{{_ 'Cancel'}}" >{{_ "Cancel"}}</button>
<button type="button" class="rc-button rc-button--submit js-clear button-block" title="{{_ 'Clear'}}" >{{_ "Clear"}}</button>
<button type="button" class="rc-button rc-button--primary js-save button-block" title="{{_ 'Save'}}">{{_ "Save"}}</button>
</div>
</form>
{{else}}
<div class="setting-block livestream--url">
{{> icon icon="permalink"}}
<a href="#" class="current-setting streaming-source-settings"> {{ streamingSource }} </a>
</div>
{{/if}}
{{/if}}
</template>
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Blaze } from 'meteor/blaze';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { auth } from '../oauth.js';
import { RocketChatAnnouncement } from '../../../lib';
import { popout } from '../../../ui-utils';
import { t } from '../../../utils';
import { settings } from '../../../settings';
import { callbacks } from '../../../../lib/callbacks';
import { hasAllPermission } from '../../../authorization';
import { Users, Rooms } from '../../../models/client';
import { dispatchToastMessage } from '../../../../client/lib/toast';
export const call = (...args) =>
new Promise(function (resolve, reject) {
Meteor.call(...args, function (error, result) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
reject(error);
return;
}
resolve(result);
});
});
export const close = (popup) =>
new Promise(function (resolve) {
const checkInterval = setInterval(() => {
if (popup.closed) {
clearInterval(checkInterval);
resolve();
}
}, 300);
});
function optionsFromUrl(url) {
const options = {};
const parsedUrl = url.match(
/(http:|https:|)\/\/(www.)?(youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/|embed\?clip=)?([A-Za-z0-9._%-]*)(\&\S+)?/,
);
options.url = url;
if (parsedUrl != null) {
options.id = parsedUrl[6];
if (parsedUrl[3].includes('youtu')) {
options.url = `https://www.youtube.com/embed/${parsedUrl[6]}`;
options.thumbnail = `https://img.youtube.com/vi/${parsedUrl[6]}/0.jpg`;
}
// @TODO add support for other urls
}
return options;
}
Template.liveStreamTab.helpers({
broadcastEnabled() {
return !!settings.get('Broadcasting_enabled');
},
streamingSource() {
return Template.instance().streamingOptions.get() ? Template.instance().streamingOptions.get().url : '';
},
streamingUnavailableMessage() {
return Template.instance().streamingOptions.get() &&
Template.instance().streamingOptions.get().message &&
Template.instance().streamingOptions.get().message !== ''
? Template.instance().streamingOptions.get().message
: t('Livestream_not_found');
},
thumbnailUrl() {
return Template.instance().streamingOptions.get() ? Template.instance().streamingOptions.get().thumbnail : '';
},
hasThumbnail() {
return (
!!Template.instance().streamingOptions.get() &&
!!Template.instance().streamingOptions.get().thumbnail &&
Template.instance().streamingOptions.get().thumbnail !== ''
);
},
hasSource() {
return (
!!Template.instance().streamingOptions.get() &&
!!Template.instance().streamingOptions.get().url &&
Template.instance().streamingOptions.get().url !== ''
);
},
canEdit() {
return hasAllPermission('edit-room', this.rid);
},
editing() {
return (
Template.instance().editing.get() ||
Template.instance().streamingOptions.get() == null ||
(Template.instance().streamingOptions.get() != null &&
(Template.instance().streamingOptions.get().url == null || Template.instance().streamingOptions.get().url === ''))
);
},
canDock() {
const livestreamTabSource = Template.instance().streamingOptions.get().url;
let popoutSource = null;
try {
if (popout.context) {
popoutSource = Blaze.getData(popout.context).data && Blaze.getData(popout.context).data.streamingSource;
}
if (popoutSource != null && livestreamTabSource === popoutSource) {
return true;
}
} catch (e) {
return false;
}
return false;
},
isPopoutOpen() {
return Template.instance().popoutOpen.get();
},
isAudioOnly() {
return Template.instance().streamingOptions.get() ? Template.instance().streamingOptions.get().isAudioOnly : false;
},
});
Template.liveStreamTab.onCreated(function () {
this.editing = new ReactiveVar(false);
this.streamingOptions = new ReactiveVar();
this.popoutOpen = new ReactiveVar(popout.context != null);
this.autorun(() => {
const room = Rooms.findOne(this.data.rid, { fields: { streamingOptions: 1 } });
this.streamingOptions.set(room.streamingOptions);
});
});
Template.liveStreamTab.onDestroyed(function () {
if (popout.docked) {
popout.close();
}
});
Template.liveStreamTab.events({
'click .js-cancel'(e, i) {
e.preventDefault();
i.editing.set(false);
},
'click .js-clear'(e, i) {
e.preventDefault();
const clearedObject = {};
Meteor.call('saveRoomSettings', this.rid, 'streamingOptions', clearedObject, function (error) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
i.editing.set(false);
i.streamingOptions.set(clearedObject);
const roomAnnouncement = new RocketChatAnnouncement().getByRoom(i.data.rid);
if (roomAnnouncement.getMessage() !== '') {
roomAnnouncement.clear();
}
return dispatchToastMessage({
type: 'success',
message: TAPi18n.__('Livestream_source_changed_succesfully'),
});
});
},
'click .js-save'(e, i) {
e.preventDefault();
const streamingOptions = {
...optionsFromUrl(i.find('[name=streaming-source]').value),
isAudioOnly: i.find('[name=streaming-audio-only]').checked,
message: i.find('[name=streaming-message]').value,
type: 'livestream',
};
Meteor.call('saveRoomSettings', this.rid, 'streamingOptions', streamingOptions, function (error) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
i.editing.set(false);
i.streamingOptions.set(streamingOptions);
if (streamingOptions.url !== '') {
new RocketChatAnnouncement({
room: i.data.rid,
message: 'Broadcast is now live. Click here to watch!',
callback: 'openBroadcast',
}).save();
} else {
const roomAnnouncement = new RocketChatAnnouncement().getByRoom(i.data.rid);
if (roomAnnouncement.getMessage() !== '') {
roomAnnouncement.clear();
}
}
return dispatchToastMessage({
type: 'success',
message: TAPi18n.__('Livestream_source_changed_succesfully'),
});
});
},
'click .streaming-source-settings'(e, i) {
e.preventDefault();
i.editing.set(true);
},
'click .js-dock'(e) {
e.stopPropagation();
popout.docked = true;
},
'click .js-close'(e, i) {
e.stopPropagation();
let popoutSource = '';
if (popout.context) {
popoutSource = Blaze.getData(popout.context).data && Blaze.getData(popout.context).data.streamingSource;
}
popout.close();
if (popoutSource !== Template.instance().streamingOptions.get().url) {
popout.open({
content: 'liveStreamView',
data: {
streamingSource: i.streamingOptions.get().url,
isAudioOnly: i.streamingOptions.get().isAudioOnly,
showVideoControls: true,
streamingOptions: i.streamingOptions.get(),
},
onCloseCallback: () => i.popoutOpen.set(false),
});
}
},
'submit .liveStreamTab__form'(e, i) {
e.preventDefault();
e.stopPropagation();
const streamingOptions = {
...optionsFromUrl(i.find('[name=streaming-source]').value),
isAudioOnly: i.find('[name=streaming-audio-only]').checked,
message: i.find('[name=streaming-message]').value,
type: 'livestream',
};
Meteor.call('saveRoomSettings', this.rid, 'streamingOptions', streamingOptions, function (error) {
if (error) {
dispatchToastMessage({ type: 'error', message: error });
return;
}
i.editing.set(false);
i.streamingOptions.set(streamingOptions);
if (streamingOptions.url !== '') {
new RocketChatAnnouncement({
room: i.data.rid,
message: 'Broadcast is now live. Click here to watch!',
callback: 'openBroadcast',
}).save();
} else {
const roomAnnouncement = new RocketChatAnnouncement().getByRoom(i.data.rid);
if (roomAnnouncement.getMessage() !== '') {
roomAnnouncement.clear();
}
}
return dispatchToastMessage({
type: 'success',
message: TAPi18n.__('Livestream_source_changed_succesfully'),
});
});
},
'click .js-popout'(e, i) {
e.preventDefault();
popout.open({
content: 'liveStreamView',
data: {
streamingSource: i.streamingOptions.get().url,
isAudioOnly: i.streamingOptions.get().isAudioOnly,
showVideoControls: true,
streamingOptions: i.streamingOptions.get(),
},
onCloseCallback: () => i.popoutOpen.set(false),
});
i.popoutOpen.set(true);
},
async 'click .js-broadcast'(e, i) {
e.preventDefault();
e.currentTarget.classList.add('loading');
try {
const user = Users.findOne({ _id: Meteor.userId() }, { fields: { 'settings.livestream': 1 } });
if (!user.settings || !user.settings.livestream) {
await auth();
}
const result = await call('livestreamGet', { rid: i.data.rid });
popout.open({
content: 'broadcastView',
data: {
...result,
showVideoControls: false,
showStreamControls: true,
},
onCloseCallback: () => i.popoutOpen.set(false),
});
} catch (e) {
console.log(e);
} finally {
e.currentTarget.classList.remove('loading');
}
},
});
callbacks.add('openBroadcast', (rid) => {
const roomData = Session.get(`roomData${rid}`);
if (!roomData) {
return;
}
popout.open({
content: 'liveStreamView',
data: {
streamingSource: roomData.streamingOptions.url,
isAudioOnly: roomData.streamingOptions.isAudioOnly,
showVideoControls: true,
streamingOptions: roomData.streamingOptions,
},
});
});
<template name="liveStreamView">
<div class="streaming-popup">
<div id="ytplayer"></div>
</div>
</template>
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
this.onYouTubePlayerAPIReady = function () {
const playerReadyEvent = new Event('playerReady');
document.querySelector('.streaming-popup').dispatchEvent(playerReadyEvent);
};
this.liveStreamPlayer = null;
Template.liveStreamView.onCreated(function () {
this.streamingOptions = new ReactiveVar(this.data.streamingOptions);
});
Template.liveStreamView.onRendered(function () {
if (window.YT) {
window.liveStreamPlayer = new window.YT.Player('ytplayer', {
width: '380',
height: '214',
videoId: this.data.streamingOptions.id || '',
playerVars: {
autoplay: 1,
controls: 0,
showinfo: 0,
enablejsapi: 1,
fs: 0,
modestbranding: 1,
rel: 0,
},
events: {
onStateChange: (e) => {
const playerStateChangedEvent = new CustomEvent('playerStateChanged', { detail: e.data });
document.querySelector('.rc-popout').dispatchEvent(playerStateChangedEvent);
},
},
});
} else {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/player_api';
tag.type = 'text/javascript';
const firstScriptTag = document.body.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
});
Template.liveStreamView.events({
'playerReady .streaming-popup'(e, i) {
window.liveStreamPlayer = new window.YT.Player('ytplayer', {
width: '380',
height: '214',
videoId: i.streamingOptions.get().id || '',
playerVars: {
autoplay: 1,
controls: 0,
showinfo: 0,
enablejsapi: 1,
fs: 0,
modestbranding: 1,
rel: 0,
},
events: {
onStateChange: (e) => {
const playerStateChangedEvent = new CustomEvent('playerStateChanged', { detail: e.data });
document.querySelector('.rc-popout').dispatchEvent(playerStateChangedEvent);
},
},
});
},
});
<template name="liveStreamBroadcast">
<button type="button" class="rc-button rc-button--stack rc-button--primary js-broadcast"> Broadcast my Camera </button>
</template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment