From 4a796974877ee0c6e32d3a3e855b72116c7cb681 Mon Sep 17 00:00:00 2001 From: Celio Rodrigues Date: Wed, 25 Aug 2021 21:15:43 -0300 Subject: [PATCH] Fixed the screenshare capability --- electron/main.js | 45 +++++++++ resources/css/screenselector.css | 146 +++++++++++++++++++++++++++++ resources/js/rambox-service-api.js | 48 +++++++++- resources/js/screenselector.js | 77 +++++++++++++++ screenselector.html | 22 +++++ 5 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 resources/css/screenselector.css create mode 100644 resources/js/screenselector.js create mode 100644 screenselector.html diff --git a/electron/main.js b/electron/main.js index 749a813a..fb68150f 100644 --- a/electron/main.js +++ b/electron/main.js @@ -576,6 +576,51 @@ ipcMain.on('toggleWin', function(event, allwaysShow) { } }); +// ScreenShare +ipcMain.on('screenShare:show', (event, screenList) => { + let tmpWindow = new BrowserWindow({ + width: 600, + height: 500, + icon: __dirname + '/../resources/Icon.ico', + autoHideMenuBar: true, + transparent: true, + show: true, + frame: false, + hasShadow: true, + webPreferences: { + nodeIntegration: true, + }, + }); + + const close = () => { + tmpWindow.close(); + tmpWindow = null; + }; + + const onCancel = () => { + event.sender.send('screenShare:cancel'); + close(); + }; + + const onShare = (_, shareId) => { + event.sender.send('screenShare:share', shareId); + close(); + }; + + ipcMain.handle('screenShare:getSources', () => screenList); + ipcMain.on('screenShare:cancelSelection', onCancel); + ipcMain.on('screenShare:selectScreen', onShare); + + tmpWindow.on('closed', () => { + ipcMain.removeHandler('screenShare:getSources'); + ipcMain.removeAllListeners('screenShare:cancelSelection'); + ipcMain.removeAllListeners('screenShare:selectScreen'); + }); + + tmpWindow.loadFile(__dirname + '/../screenselector.html'); +}); + + // Proxy if ( config.get('proxy') ) { app.commandLine.appendSwitch('proxy-server', config.get('proxyHost')+':'+config.get('proxyPort')); diff --git a/resources/css/screenselector.css b/resources/css/screenselector.css new file mode 100644 index 00000000..c3f5314b --- /dev/null +++ b/resources/css/screenselector.css @@ -0,0 +1,146 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; +} + +.screen-selector { + background-color: #fff; + width: 100%; + height: 100%; + border-radius: 5px; + border: solid 1px #cdcdcd; + box-sizing: border-box; +} + +.screen-selector ul.type { + display: inline-block; + padding: 0; + margin: 0; + border-bottom: solid 1px #cdcdcd; + width: 100%; + -webkit-app-region: drag; +} + +.screen-selector ul.type li { + display: inline-block; + height: 40px; + line-height: 40px; + color: #65696c; + border-bottom: 2px solid transparent; + font-family: sans-serif; + font-weight: bold; + padding: 0 15px; + cursor: pointer; + -webkit-app-region: no-drag; +} + +.screen-selector ul.type li.active, .screen-selector ul.type li:hover { + color: #426ba3; + border-color: #426ba3; +} + +.screen-selector > .content { + height: calc(100% - 92px); +} + +.screen-selector ul.preview { + display: flex; + margin: 0; + padding: 0; + width: 100%; + max-height: calc(100% - 45px); + box-sizing: border-box; + padding: 10px; + overflow-y: auto; + flex-wrap: wrap; + list-style-type: none; +} + +.screen-selector ul.preview li { + display: inline-block; + box-sizing: border-box; + width: calc(33% - 1px); + cursor: pointer; + padding: 5px; +} + +.screen-selector ul.preview li .content { + display: flex; + box-sizing: border-box; + padding: 10px; + border: solid 2px transparent; + border-radius: 5px; + height: 100%; + flex-direction: column; +} + +.screen-selector ul.preview li.active .content, +.screen-selector ul.preview li:hover .content { + border-color: #88abdb; +} + +.screen-selector ul.preview li.active .content { + color: #000; + height: 100%; +} + +.screen-selector ul.preview li .content .img-wrapper { + display: flex; + height: 100%; + align-items: center; + background: #f0f0f0; +} + +.screen-selector ul.preview li .content img { + display: inline-block; + width: 100%; +} + +.screen-selector ul.preview li .content span { + display: inline-block; + width: 100%; + font-family: sans-serif; + text-align: center; + font-size: 14px; + margin-top: 10px; + color: #333; + box-sizing: border-box; +} + +.screen-selector .footer { + display: inline-block; + width: 100%; + border-top: solid 1px #cdcdcd; + +} + +.screen-selector .footer button { + float: right; + margin-top: 10px; + margin-right: 10px; + border: none; + border-radius: 5px; + padding: 5px 10px; + font-size: 14px; + cursor: pointer; + font-weight: bold; +} + +.screen-selector .footer button#cancel { + border: solid 1px #cdcdcd; + background: #fff; + color: #1c73e4; +} + +.screen-selector .footer button#share { + border: solid 1px #186ddd; + background: #1c73e4; + color: #fff; +} + +.screen-selector .footer button#share[disabled] { + background: #666; + cursor: default; +} diff --git a/resources/js/rambox-service-api.js b/resources/js/rambox-service-api.js index 30d6473a..f4dbc01d 100644 --- a/resources/js/rambox-service-api.js +++ b/resources/js/rambox-service-api.js @@ -2,7 +2,7 @@ * This file is loaded in the service web views to provide a Rambox API. */ -const { ipcRenderer } = require('electron'); +const { desktopCapturer, ipcRenderer } = require('electron'); const { ContextMenuBuilder, ContextMenuListener } = require('electron-contextmenu-wrapper'); /** @@ -61,3 +61,49 @@ mousetrap.bind(process.platform === 'darwin' ? ['command+left', 'command+right'] if (location.href.indexOf('slack.com') !== -1) return; e.key === 'ArrowLeft' ? history.back() : history.forward(); }); + + +// ScreenShare +window.navigator.mediaDevices.getDisplayMedia = () => + new Promise(async (resolve, reject) => { + try { + const sources = await desktopCapturer.getSources({ + types: ['screen', 'window'], + }); + + const unlisten = () => { + ipcRenderer.removeAllListeners('screenShare:cancel'); + ipcRenderer.removeAllListeners('screenShare:share'); + }; + + ipcRenderer.on('screenShare:cancel', () => { + unlisten(); + reject(new Error('Cancelled by user')); + }); + + ipcRenderer.on('screenShare:share', (_, shareId) => { + unlisten(); + window.navigator.mediaDevices + .getUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: shareId, + }, + }, + }) + .then(stream => resolve(stream)); + }); + + const mappedSources = sources.map(it => ({ + id: it.id, + name: it.name, + thumbnail: it.thumbnail.toDataURL(), + })); + + ipcRenderer.send('screenShare:show', mappedSources); + } catch (err) { + reject(err); + } + }); diff --git a/resources/js/screenselector.js b/resources/js/screenselector.js new file mode 100644 index 00000000..9f5b7a1a --- /dev/null +++ b/resources/js/screenselector.js @@ -0,0 +1,77 @@ +const { ipcRenderer } = require('electron'); + +let sourceList, contentList, typeButtons, shareButton, cancelButton, activeItem, activeTab = 'screen'; + +const getItemDOM = item => { + const li = document.createElement('li'); + li.setAttribute('data-id', item.id); + li.innerHTML = `
${item.name}
`; + li.addEventListener('click', onItemClick, false); + return li; +}; + +const updateTab = () => { + const sources = sourceList.filter(it => it.id.indexOf(activeTab) === 0); + contentList.innerHTML = ''; + sources.forEach(source => contentList.appendChild(getItemDOM(source))); +}; + +const bindTypeClick = () => { + typeButtons.forEach(it => it.addEventListener('click', onChangeType, false)); +}; + +const bindActionsClick = () => { + shareButton.addEventListener('click', onShareClick, false); + cancelButton.addEventListener('click', onCancelClick, false); +}; + +const onChangeType = event => { + event.preventDefault(); + if (!event.target.classList.contains('active')) { + activeTab = event.target.dataset.type; + typeButtons.forEach(it => it.classList.remove('active')); + event.target.classList.add('active'); + updateTab(); + } +}; + +const onItemClick = event => { + event.preventDefault(); + if (!event.currentTarget.classList.contains('active')) { + activeItem = event.currentTarget.dataset.id; + document.querySelectorAll('.preview li').forEach(it => it.classList.remove('active')); + event.currentTarget.classList.add('active'); + changeShareState(); + } +}; + +const onCancelClick = event => { + event.preventDefault(); + ipcRenderer.send('screenShare:cancelSelection'); +}; + +const onShareClick = event => { + event.preventDefault(); + if (activeItem) { + ipcRenderer.send('screenShare:selectScreen', activeItem); + } +}; + +const changeShareState = () => { + if (shareButton.getAttribute('disabled') !== null) { + shareButton.removeAttribute('disabled'); + } +}; + +window.addEventListener('load', async () => { + sourceList = await ipcRenderer.invoke('screenShare:getSources'); + + contentList = document.querySelector('.preview'); + typeButtons = document.querySelectorAll('.type li'); + shareButton = document.querySelector('#share'); + cancelButton = document.querySelector('#cancel'); + + bindTypeClick(); + bindActionsClick(); + updateTab(); +}, false); diff --git a/screenselector.html b/screenselector.html new file mode 100644 index 00000000..61c43a95 --- /dev/null +++ b/screenselector.html @@ -0,0 +1,22 @@ + + + + + + +
+ +
+
    +
    + +
    + + +