From bfb924a1607d55b3e5995919d9bf8dc536751459 Mon Sep 17 00:00:00 2001 From: Gabriel Delavald Date: Fri, 12 Jan 2018 16:43:30 -0200 Subject: [PATCH] Added popout drag-n-drop --- .meteor/versions | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 6 +- .../client/views/liveStreamTab.html | 16 ++-- .../client/views/liveStreamTab.js | 94 ++++++++++++------- .../client/views/liveStreamView.html | 10 +- .../client/views/liveStreamView.js | 11 +++ packages/rocketchat-livestream/package.js | 2 +- .../client/imports/components/popout.css | 21 ++--- .../client/views/app/popout.html | 11 +-- .../rocketchat-ui/client/views/app/popout.js | 83 ++++++++-------- 10 files changed, 146 insertions(+), 110 deletions(-) diff --git a/.meteor/versions b/.meteor/versions index 7d875e769b8..9ef61914a64 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -170,7 +170,7 @@ rocketchat:katex@0.0.1 rocketchat:ldap@0.0.1 rocketchat:lib@0.0.1 rocketchat:livechat@0.0.1 -rocketchat:livestream@0.0.3 +rocketchat:livestream@0.0.5 rocketchat:logger@0.0.1 rocketchat:login-token@1.0.0 rocketchat:mailer@0.0.1 diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 1367d767ccb..894112f361d 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1127,10 +1127,12 @@ "Livechat_title_color": "Livechat Title Background Color", "Livechat_Users": "Livechat Users", "Livestream_title": "LiveStream", - "Livestream_not_found": "Livestream not found", + "Livestream_not_found": "Livestream not Available", "Livestream_popout": "Open Livestream in Popout", - "Livestream_url": "Livestream url", + "Livestream_url": "Livestream source url", "Livestream_source_changed_succesfully": "Livestream source changed successfully", + "Livestream_undocked": "Livestream is undocked", + "Livestream_switch_to_room": "Switch to current room's livestream", "Load_more": "Load more", "Loading...": "Loading...", "Loading_more_from_history": "Loading more from history", diff --git a/packages/rocketchat-livestream/client/views/liveStreamTab.html b/packages/rocketchat-livestream/client/views/liveStreamTab.html index 9c4d60af0d2..92e218e45a8 100644 --- a/packages/rocketchat-livestream/client/views/liveStreamTab.html +++ b/packages/rocketchat-livestream/client/views/liveStreamTab.html @@ -9,8 +9,8 @@ {{#if editing}}
- - + +
{{else}}
@@ -19,14 +19,12 @@ {{/if}} {{/if}} {{#if hasSource}} - {{#unless isDocked}} - {{#if canDock}} -
- {{_ "Livestream_undocked" }} -
- {{else}} + {{#unless isPopoutOpen}} + + {{else}} + {{#unless canDock}} - {{/if}} + {{/unless}} {{/unless}} {{else}} {{_ "Livestream_not_found" }} diff --git a/packages/rocketchat-livestream/client/views/liveStreamTab.js b/packages/rocketchat-livestream/client/views/liveStreamTab.js index 860a5c81015..da5e560d97d 100644 --- a/packages/rocketchat-livestream/client/views/liveStreamTab.js +++ b/packages/rocketchat-livestream/client/views/liveStreamTab.js @@ -2,25 +2,33 @@ import toastr from 'toastr'; function parseUrl(url) { + const options = {}; const parsedUrl = url.match(/(http:|https:|)\/\/(clips.|player.|www.)?(twitch\.tv|vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/|embed\?clip=)?([A-Za-z0-9._%-]*)(\&\S+)?/); - let source = url; + options.url = url; if (parsedUrl != null) { if (parsedUrl[3].includes('youtu')) { - source = `https://www.youtube.com/embed/${ parsedUrl[6] }?showinfo=0`; + options.url = `https://www.youtube.com/embed/${ parsedUrl[6] }?showinfo=0`; + options.thumbnail = `https://img.youtube.com/vi/${ parsedUrl[6] }/0.jpg`; } else if (parsedUrl[3].includes('vimeo')) { - source = `https://player.vimeo.com/video/${ parsedUrl[6] }`; + options.url = `https://player.vimeo.com/video/${ parsedUrl[6] }`; } else if (parsedUrl[3].includes('twitch')) { - source = `http://player.twitch.tv/?channel=${ parsedUrl[6] }`; + options.url = `http://player.twitch.tv/?channel=${ parsedUrl[6] }`; } // @TODO add support for other urls - return source; } + return options; } Template.liveStreamTab.helpers({ streamingSource() { return Template.instance().streamingOptions.get() ? Template.instance().streamingOptions.get().url : ''; }, + 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 !== ''; }, @@ -34,25 +42,28 @@ Template.liveStreamTab.helpers({ const livestreamTabSource = Template.instance().streamingOptions.get().url; let popoutSource = null; try { - popoutSource = Blaze.getData(popout.context).data && Blaze.getData(popout.context).data.streamingSource; + if (popout.context) { + popoutSource = Blaze.getData(popout.context).data && Blaze.getData(popout.context).data.streamingSource; + } } catch (e) { return false; } finally { - if (livestreamTabSource === popoutSource) { + if (popoutSource != null && livestreamTabSource === popoutSource) { return true; } else { return false; } } }, - isDocked() { - return popout.docked; + isPopoutOpen() { + return Template.instance().popoutOpen.get(); } }); Template.liveStreamTab.onCreated(function() { this.editing = new ReactiveVar(false); this.streamingOptions = new ReactiveVar(); + this.popoutOpen = new ReactiveVar(popout.context != null); this.autorun(() => { const room = RocketChat.models.Rooms.findOne(this.data.rid, { fields: { streamingOptions : 1 } }); @@ -60,14 +71,15 @@ Template.liveStreamTab.onCreated(function() { }); }); Template.liveStreamTab.onRendered(function() { - if (popout.context == null && (!!this.streamingOptions.get().url && this.streamingOptions.get().url !== '')) { - popout.open({ - content: 'liveStreamView', - data: { - 'streamingSource': this.streamingOptions.get().url - } - }); - } + // console.log('rendered'); + // if (popout.context == null && (!!this.streamingOptions.get().url && this.streamingOptions.get().url !== '')) { + // popout.open({ + // content: 'liveStreamView', + // data: { + // 'streamingSource': this.streamingOptions.get().url + // } + // }); + // } }); Template.liveStreamTab.onDestroyed(function() { @@ -85,9 +97,7 @@ Template.liveStreamTab.events({ 'click .js-save'(e, i) { e.preventDefault(); - const streamingOptions = { - url: parseUrl(i.find('[name=streamingOptions]').value) - }; + const streamingOptions = parseUrl(i.find('[name=streamingOptions]').value); Meteor.call('saveRoomSettings', this.rid, 'streamingOptions', streamingOptions, function(err) { if (err) { @@ -115,23 +125,39 @@ Template.liveStreamTab.events({ 'streamingSource': Template.instance().streamingOptions.get().url } }); + }, + 'submit [name=streamingOptions]'(e) { + e.preventDefault(); + }, + 'click .js-popout'(e, i) { + e.preventDefault(); + popout.open({ + content: 'liveStreamView', + data: { + 'streamingSource': Template.instance().streamingOptions.get().url + } + }); + i.popoutOpen.set(true); } }); -RocketChat.callbacks.add('enter-room', function() { +RocketChat.callbacks.add('roomExit', function() { if (popout.context != null && popout.docked) { - const room = RocketChat.models.Rooms.findOne(Session.get('openedRoom'), { fields: { streamingOptions : 1 } }); - - if (room.streamingOptions && room.streamingOptions.url !== popout.config.data.streamingSource) { - popout.close(); - if (document.querySelector('.flex-tab-bar .tab-button.active') && document.querySelector('.flex-tab-bar .tab-button.active').title === 'Livestream') { - popout.open({ - content: 'liveStreamView', - data: { - 'streamingSource': room.streamingOptions.url - } - }); - } - } + popout.close(); } }, RocketChat.callbacks.priority.HIGH, 'close-docked-popout'); + +RocketChat.callbacks.add('enter-room', function() { + // console.log('enter-room'); + // const room = RocketChat.models.Rooms.findOne(Session.get('openedRoom'), { fields: { streamingOptions : 1 } }); + // if (popout.docked && (room.streamingOptions && room.streamingOptions.url !== popout.config.data.streamingSource)) { + // if (document.querySelector('.flex-tab-bar .tab-button.active') && document.querySelector('.flex-tab-bar .tab-button.active').title === 'Livestream') { + // popout.open({ + // content: 'liveStreamView', + // data: { + // 'streamingSource': room.streamingOptions.url + // } + // }); + // } + // } +}, RocketChat.callbacks.priority.HIGH, 'reopen-popout'); diff --git a/packages/rocketchat-livestream/client/views/liveStreamView.html b/packages/rocketchat-livestream/client/views/liveStreamView.html index 12fb159cc63..2b6d5631f8b 100644 --- a/packages/rocketchat-livestream/client/views/liveStreamView.html +++ b/packages/rocketchat-livestream/client/views/liveStreamView.html @@ -3,10 +3,10 @@ - - - - - + + + + +
diff --git a/packages/rocketchat-livestream/client/views/liveStreamView.js b/packages/rocketchat-livestream/client/views/liveStreamView.js index e69de29bb2d..8ca25c85427 100644 --- a/packages/rocketchat-livestream/client/views/liveStreamView.js +++ b/packages/rocketchat-livestream/client/views/liveStreamView.js @@ -0,0 +1,11 @@ +Template.liveStreamView.events({ + 'click .streaming-object'(e) { + e.stopPropagation(); + console.log('clicked video'); + }, + 'click .youtube-player'(e) { + e.stopPropagation(); + e.preventDefault(); + console.log('youtube player '); + } +}); diff --git a/packages/rocketchat-livestream/package.js b/packages/rocketchat-livestream/package.js index eb958c2831a..28ff30e5c1c 100644 --- a/packages/rocketchat-livestream/package.js +++ b/packages/rocketchat-livestream/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'rocketchat:livestream', - version: '0.0.3', + version: '0.0.5', summary: 'Embed livestream to Rocket.Chat channels', git: '' }); diff --git a/packages/rocketchat-theme/client/imports/components/popout.css b/packages/rocketchat-theme/client/imports/components/popout.css index 48b56099e65..1af53f888f7 100644 --- a/packages/rocketchat-theme/client/imports/components/popout.css +++ b/packages/rocketchat-theme/client/imports/components/popout.css @@ -12,24 +12,16 @@ position: absolute; z-index: 10; - top: calc(var(--header-min-height) + 10px); + top: calc(var(--header-min-height) + 135px); left: calc(var(--sidebar-width) + 10px); display: flex; align-items: center; justify-content: center; - } - &--docked { - top: calc(var(--header-min-height) + 135px); - right: 55px; - left: auto; - & .contextual-bar__close { - display: none; - } - & .rc-popout { - box-shadow: none; - } + -webkit-user-drag: element; + -khtml-user-drag: element; + } &__title { @@ -67,6 +59,11 @@ animation: dropdown-show 0.1s cubic-bezier(0.45, 0.05, 0.55, 0.95); align-items: stretch; + + & .youtube-player { + height: inherit; + width: inherit; + } } &__content-icon { diff --git a/packages/rocketchat-ui/client/views/app/popout.html b/packages/rocketchat-ui/client/views/app/popout.html index 7eff5d0d241..89f7b359ce7 100644 --- a/packages/rocketchat-ui/client/views/app/popout.html +++ b/packages/rocketchat-ui/client/views/app/popout.html @@ -1,6 +1,6 @@ diff --git a/packages/rocketchat-ui/client/views/app/popout.js b/packages/rocketchat-ui/client/views/app/popout.js index b77704f1127..6a13936f3c7 100644 --- a/packages/rocketchat-ui/client/views/app/popout.js +++ b/packages/rocketchat-ui/client/views/app/popout.js @@ -2,7 +2,9 @@ this.popout = { context: null, - docked: true, + isAudioOnly: false, + x: 0, + y: 0, open(config = {}, fn) { this.close(); this.context = Blaze.renderWithData(Template.popout, config, document.body); @@ -12,8 +14,8 @@ this.popout = { if (config.timer) { this.timer = setTimeout(() => this.close(), config.timer); } - if (config.docked != null) { - this.docked = config.docked; + if (config.isAudioOnly) { + this.isAudioOnly = config.isAudioOnly; } }, close() { @@ -25,6 +27,20 @@ this.popout = { if (this.timer) { clearTimeout(this.timer); } + }, + dragover(event) { + const e = event.originalEvent || event; + e.dataTransfer.dropEffect = 'move'; + e.preventDefault(); + }, + drop(event) { + const e = event.originalEvent || event; + e.preventDefault(); + const popoutElement = document.querySelector('.rc-popout-wrapper'); + const positionTop = e.clientY - popout.y; + const positionLeft = e.clientX - popout.x; + popoutElement.style.left = `${ positionLeft >= 0 ? positionLeft : 0 }px`; + popoutElement.style.top = `${ positionTop >= 0 ? positionTop : 0 }px`; } }; @@ -32,11 +48,8 @@ Template.popout.helpers({ state() { return Template.instance().isMinimized.get() ? 'closed' : 'open'; }, - style() { - return Template.instance().isDocked.get() ? 'docked' : 'undocked'; - }, - isDocked() { - return Template.instance().isDocked.get(); + type() { + return 'video'; //or 'audio' } }); @@ -46,10 +59,18 @@ Template.popout.onRendered(function() { } }); Template.popout.onCreated(function() { - this.isDocked = new ReactiveVar(popout.docked); this.isMinimized = new ReactiveVar(false); + this.isAudioOnly = new ReactiveVar(popout.isAudioOnly); + + document.body.addEventListener('dragover', popout.dragover, true); + document.body.addEventListener('drop', popout.drop, true); }); + Template.popout.onDestroyed(function() { + popout.context = null; + document.body.removeEventListener('dragover', popout.dragover, true); + document.body.removeEventListener('drop', popout.drop, true); + }); Template.popout.events({ @@ -58,45 +79,31 @@ Template.popout.events({ e.stopPropagation(); popout.close(); }, - 'click .js-close'(e, i) { + 'click .js-close'(e) { e.stopPropagation(); - popout.docked = true; - const livestreamTab = document.querySelector('.flex-tab--livestream'); - let livestreamTabSource; - let popoutSource; - try { - livestreamTabSource = Blaze.getView(livestreamTab).templateInstance().streamingOptions.get().url; - popoutSource = Blaze.getData(popout.context).data && Blaze.getData(popout.context).data.streamingSource; - if (livestreamTab == null || livestreamTabSource !== popoutSource) { - popout.close(); - popout.open({ - content: 'liveStreamView', - data: { - 'streamingSource': livestreamTabSource - } - }); - } else { - i.isDocked.set(true); - } - } catch (e) { - console.log(e); - popout.close(); - } + popout.close(); }, 'click .js-minimize'(e, i) { e.stopPropagation(); if (i.isMinimized.get()) { i.isMinimized.set(false); - document.querySelector('.rc-popout object').height = '350px'; + document.querySelector('.streaming-object').height = '350px'; } else { i.isMinimized.set(true); - document.querySelector('.rc-popout object').height = '40px'; + document.querySelector('.streaming-object').height = '40px'; } }, - 'click .js-dock'(e, i) { - e.stopPropagation(); - popout.docked = !i.isDocked.get(); - i.isDocked.set(!i.isDocked.get()); + 'dragstart .rc-popout-wrapper'(event) { + const e = event.originalEvent || event; + const url = this.data.streamingSource || '.rc-popout-wrapper'; + popout.x = e.offsetX; + popout.y = e.offsetY; + e.dataTransfer.setData('application/x-moz-node', e.currentTarget); + e.dataTransfer.setData('text/plain', url); + e.dataTransfer.effectAllowed = 'move'; + }, + 'dragend .rc-popout-wrapper'(event) { + event.preventDefault(); } });