diff --git a/README.md b/README.md index b6f95bbd..9c0c9d1d 100644 --- a/README.md +++ b/README.md @@ -170,3 +170,7 @@ There is an example file [here](documentation/examples/nginx/site.conf). NB: this example files also serve the static html files with converseJS. NB: it is recommanded to change ```Access-Control-Allow-Origin``` to something else that ```"*"```. + +## Credits + +Thanks to David Revoy for his work on Peertube's mascot, [Sepia](https://www.davidrevoy.com/index.php?tag/peertube). diff --git a/assets/style.css b/assets/style.css index 8f0c0081..42f448bb 100644 --- a/assets/style.css +++ b/assets/style.css @@ -1,4 +1,47 @@ -iframe.peertube-plugin-livechat { +#peertube-plugin-livechat-container { + display: flex; + flex-direction: column; +} + +.peertube-plugin-livechat-buttons { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-end; +} + +.peertube-plugin-livechat-button { + border: 1px solid black; + background-color: var(--mainBackgroundColor); + min-height: 32px; + min-width: 32px; + padding: 0; +} + +.peertube-plugin-livechat-button-icon { + background-size: 32px 32px; + background-repeat: no-repeat; + background-color: transparent; + background-position: center center; + display: inline-block; + height: 100%; + width: 100%; +} + +[peertube-plugin-livechat-state="initializing"] { + display: none; +} + +[peertube-plugin-livechat-state="open"] .peertube-plugin-livechat-button-open { + display: none; +} + +[peertube-plugin-livechat-state="closed"] .peertube-plugin-livechat-button-close { + display: none; +} + +#peertube-plugin-livechat-container iframe { border: 1px solid black; min-height: 30vh; + height: 100%; } diff --git a/client/common-client-plugin.js b/client/common-client-plugin.js index dde51eaa..3e233faf 100644 --- a/client/common-client-plugin.js +++ b/client/common-client-plugin.js @@ -5,13 +5,10 @@ function register ({ registerHook, _peertubeHelpers }) { registerHook({ target: 'action:router.navigation-end', handler: () => { - const el = document.querySelector('.peertube-plugin-livechat-init') - if (el) { - el.classList.remove('peertube-plugin-livechat-init') + const container = document.querySelector('#peertube-plugin-livechat-container') + if (container) { + container.remove() } - - document.querySelectorAll('.peertube-plugin-livechat-stuff') - .forEach(dom => dom.remove()) } }) } diff --git a/client/videowatch-client-plugin.js b/client/videowatch-client-plugin.js index 7c227dfd..2ce450b4 100644 --- a/client/videowatch-client-plugin.js +++ b/client/videowatch-client-plugin.js @@ -63,21 +63,30 @@ function register ({ registerHook, peertubeHelpers }) { return iframeUri } - function displayButton (buttons, name, label, callback) { + function displayButton (buttonContainer, name, label, callback, icon) { const button = document.createElement('button') button.classList.add( - 'action-button', - 'peertube-plugin-livechat-stuff', + 'peertube-plugin-livechat-button', 'peertube-plugin-livechat-button-' + name ) - button.setAttribute('type', 'button') - button.textContent = label button.onclick = callback - buttons.prepend(button) + if (icon) { + const iconUrl = peertubeHelpers.getBaseStaticRoute() + '/images/' + icon + const iconEl = document.createElement('span') + iconEl.classList.add('peertube-plugin-livechat-button-icon') + iconEl.setAttribute('style', + 'background-image: url(\'' + iconUrl + '\');' + ) + button.prepend(iconEl) + button.setAttribute('title', label) + } else { + button.textContent = label + } + buttonContainer.append(button) } - function displayChatButtons (peertubeHelpers, uuid, showOpenBlank) { - logger.log('Adding buttons in the DOM...') + function insertChatDom (container, peertubeHelpers, uuid, showOpenBlank) { + logger.log('Adding livechat in the DOM...') const p = new Promise((resolve, reject) => { Promise.all([ peertubeHelpers.translate('Open chat'), @@ -87,37 +96,31 @@ function register ({ registerHook, peertubeHelpers }) { const labelOpen = labels[0] const labelOpenBlank = labels[1] const labelClose = labels[2] - const buttons = document.querySelector('.video-actions') const iframeUri = getIframeUri(uuid) if (!iframeUri) { return reject(new Error('No uri, cant display the buttons.')) } + + const buttonContainer = document.createElement('div') + buttonContainer.classList.add('peertube-plugin-livechat-buttons') + container.append(buttonContainer) + + displayButton(buttonContainer, 'open', labelOpen, () => openChat(), 'talking.png') if (showOpenBlank) { - displayButton(buttons, 'openblank', labelOpenBlank, () => { + displayButton(buttonContainer, 'openblank', labelOpenBlank, () => { closeChat() window.open(iframeUri) - }) + }, 'talking-new-window.png') } - displayButton(buttons, 'open', labelOpen, () => openChat()) - displayButton(buttons, 'close', labelClose, () => closeChat()) + displayButton(buttonContainer, 'close', labelClose, () => closeChat(), 'bye.png') - toggleShowHideButtons(null) resolve() }) }) return p } - function toggleShowHideButtons (chatOpened) { - // showing/hiding buttons... - document.querySelectorAll('.peertube-plugin-livechat-button-open') - .forEach(button => (button.style.display = (chatOpened === true || chatOpened === null ? 'none' : ''))) - - document.querySelectorAll('.peertube-plugin-livechat-button-close') - .forEach(button => (button.style.display = (chatOpened === false || chatOpened === null ? 'none' : ''))) - } - function openChat () { const p = new Promise((resolve, reject) => { const uuid = lastUUID @@ -135,25 +138,27 @@ function register ({ registerHook, peertubeHelpers }) { const additionalStyles = settings['chat-style'] || '' logger.info('Opening the chat...') - const videoWrapper = document.querySelector('#video-wrapper') + const container = document.getElementById('peertube-plugin-livechat-container') + if (!container) { + logger.error('Cant found the livechat container.') + return reject(new Error('Cant found the livechat container')) + } + + if (container.querySelector('iframe')) { + logger.error('Seems that there is already an iframe in the container.') + return reject(new Error('Seems that there is already an iframe in the container.')) + } // Creating the iframe... const iframe = document.createElement('iframe') iframe.setAttribute('src', iframeUri) - iframe.classList.add( - 'peertube-plugin-livechat', - 'peertube-plugin-livechat-stuff', - 'peertube-plugin-livechat-iframe-stuff' - ) iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms') iframe.setAttribute('frameborder', '0') if (additionalStyles) { iframe.setAttribute('style', additionalStyles) } - videoWrapper.append(iframe) - - // showing/hiding buttons... - toggleShowHideButtons(true) + container.append(iframe) + container.setAttribute('peertube-plugin-livechat-state', 'open') resolve() }) @@ -161,25 +166,32 @@ function register ({ registerHook, peertubeHelpers }) { } function closeChat () { - document.querySelectorAll('.peertube-plugin-livechat-iframe-stuff') + const container = document.getElementById('peertube-plugin-livechat-container') + if (!container) { + logger.error('Cant close livechat, container not found.') + return + } + container.querySelectorAll('iframe') .forEach(dom => dom.remove()) - // showing/hiding buttons... - toggleShowHideButtons(false) + container.setAttribute('peertube-plugin-livechat-state', 'closed') } function initChat () { - const el = document.querySelector('#videojs-wrapper') - if (!el) { + const videoWrapper = document.querySelector('#video-wrapper') + if (!videoWrapper) { logger.error('The required div is not present in the DOM.') return } - if (el.classList.contains('peertube-plugin-livechat-init')) { + let container = videoWrapper.querySelector('#peertube-plugin-livechat-container') + if (container) { logger.log('The chat seems already initialized...') return } - // Adding a custom class in the dom, so we know initChat was already called. - el.classList.add('peertube-plugin-livechat-init') + container = document.createElement('div') + container.setAttribute('id', 'peertube-plugin-livechat-container') + container.setAttribute('peertube-plugin-livechat-state', 'initializing') + videoWrapper.append(container) peertubeHelpers.getSettings().then(s => { settings = s @@ -214,11 +226,11 @@ function register ({ registerHook, peertubeHelpers }) { return } - displayChatButtons(peertubeHelpers, uuid, !!settings['chat-open-blank']).then(() => { + insertChatDom(container, peertubeHelpers, uuid, !!settings['chat-open-blank']).then(() => { if (settings['chat-auto-display']) { openChat() } else { - toggleShowHideButtons(false) + container.setAttribute('peertube-plugin-livechat-state', 'closed') } }) }) diff --git a/package-lock.json b/package-lock.json index 5bc50721..35bc2119 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "peertube-plugin-livechat", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2920,6 +2920,12 @@ "readable-stream": "^2.0.1" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -3147,6 +3153,73 @@ "dev": true, "optional": true }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -3464,6 +3537,12 @@ "dev": true, "optional": true }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -3938,6 +4017,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -4268,6 +4353,17 @@ "strip-ansi": "^5.1.0" } }, + "string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, "string.prototype.trimend": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", diff --git a/package.json b/package.json index 762bf1a6..efe0de6e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", + "npm-run-all": "^4.1.5", "webpack": "^4.41.2", "webpack-cli": "^3.3.10" }, @@ -43,13 +44,18 @@ ], "library": "./main.js", "scripts": { + "clean": "rm -rf dist/*", "prepare": "npm run build", - "build": "mkdir -p dist/conversejs && cp -r node_modules/converse.js/dist/* dist/conversejs/ && webpack --mode=production", + "build:converse": "mkdir -p dist/conversejs && cp -r node_modules/converse.js/dist/* dist/conversejs/", + "build:images": "mkdir -p dist/images && cp public/images/* dist/images/", + "build:webpack": "webpack --mode=production", + "build": "npm-run-all -s clean -p build:converse build:images build:webpack", "lint": "npx eslint --ext .js ." }, "staticDirs": { "static": "dist/static", - "conversejs": "dist/conversejs/" + "conversejs": "dist/conversejs/", + "images": "dist/images/" }, "translations": { "fr-FR": "./languages/fr.json" diff --git a/public/images/.gitkeep b/public/images/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/public/images/bye.png b/public/images/bye.png new file mode 100644 index 00000000..9238964e Binary files /dev/null and b/public/images/bye.png differ diff --git a/public/images/bye.svg b/public/images/bye.svg new file mode 100644 index 00000000..777aebfe --- /dev/null +++ b/public/images/bye.svg @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/talking-new-window.png b/public/images/talking-new-window.png new file mode 100644 index 00000000..69a6638e Binary files /dev/null and b/public/images/talking-new-window.png differ diff --git a/public/images/talking-new-window.svg b/public/images/talking-new-window.svg new file mode 100644 index 00000000..dc999847 --- /dev/null +++ b/public/images/talking-new-window.svg @@ -0,0 +1,533 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/talking.png b/public/images/talking.png new file mode 100644 index 00000000..9c0791e2 Binary files /dev/null and b/public/images/talking.png differ diff --git a/public/images/talking.svg b/public/images/talking.svg new file mode 100644 index 00000000..88032277 --- /dev/null +++ b/public/images/talking.svg @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +