diff --git a/bun.lockb b/bun.lockb index ab143e7..2e137e3 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/client/script.js b/client/script.js index 3150ab7..4245789 100644 --- a/client/script.js +++ b/client/script.js @@ -98,9 +98,24 @@ function getMessage(m) { break case 'editBoardMetas': - sendAction('editBoardMetas', { prop: 'opacity', value: 0.5 }) + if (data.prop === 'imageUrl') { + document.body.style.backgroundImage = data.value ? `url("${data.value}")` : 'none' + + if (data.value.startsWith('/images/')) { + $('.bg').removeClass('selected') + $(`.bg[src="${data.value}"]`).addClass('selected') + } else { + $('.bg').removeClass('selected') + $('.bgurl').val(data.value) + } + } else if (data.prop === 'opacity') { + document.documentElement.style.setProperty('--board-opacity', data.value) + const slider = document.getElementById('opacity-range') + if (slider) slider.value = data.value + } break + case 'moveCard': moveCard($(`#${data.id}`), data.position) break @@ -740,6 +755,18 @@ $(() => { // disable image dragging // window.ondragstart = function() { return false; }; + const slider = document.getElementById('opacity-range'); + if (slider) { + slider.addEventListener('input', function () { + document.documentElement.style.setProperty('--board-opacity', slider.value); + sendAction('editBoardMetas', { + id: 'opacity', + prop: 'opacity', + value: slider.value + }); + }); + } + if (boardInitialized === false) blockUI('') // setTimeout($.unblockUI, 2000); @@ -1125,25 +1152,44 @@ $(() => { return false }) $('.backgrounds .bg').on('click', function() { + const imageUrl = `/${$(this).attr('src')}` + if ($(this).hasClass('selected')) { $('body').css('background-image', 'none') $(this).removeClass('selected') + sendAction('editBoardMetas', { + id: 'background', + prop: 'imageUrl', + value: '' + }) } else { $('.selected').removeClass('selected') $('.bgurl').val('') - $('body').css('background-image', `url("/${$(this).attr('src')}")`) + $('body').css('background-image', `url("${imageUrl}")`) $(this).addClass('selected') + sendAction('editBoardMetas', { + id: 'background', + prop: 'imageUrl', + value: imageUrl + }) } }) + $('.bgurl').on('change', function() { const url = $(this).val() if (url) { $('.selected').removeClass('selected') $('body').css('background-image', `url("${url}")`) + sendAction('editBoardMetas', { + id: 'background', + prop: 'imageUrl', + value: url + }) } }) }) + function toggleNav(target) { if ($('#site-wrapper').hasClass('show-nav') && target === false) { $('#site-wrapper').removeClass('show-nav') @@ -1156,3 +1202,9 @@ function toggleNav(target) { } return false } + +function takescreenshot(selector) { + html2canvas(document.querySelector(selector)).then(canvas => { + document.body.appendChild(canvas) + }); +} diff --git a/lib/data/redis.js b/lib/data/redis.js index e8a3b47..9adc0fe 100644 --- a/lib/data/redis.js +++ b/lib/data/redis.js @@ -112,21 +112,31 @@ db.prototype = { }, // Board metadata commands - createBoardMetas(room, id, board) { - const boardString = JSON.stringify(board) + createBoardMetas(room, id, metaObj) { redisClient.hset( `${REDIS_PREFIX}-room:${room}-board`, id, - boardString + JSON.stringify(metaObj) ) }, + getBoardMetas(room, callback) { redisClient.hgetall(`${REDIS_PREFIX}-room:${room}-board`, (err, res) => { - console.log('board metas', res) - callback(JSON.parse(res)) + const metas = {} + if (res) { + for (const key in res) { + try { + metas[key] = JSON.parse(res[key]) + } catch (e) { + console.error('JSON error in board metas', key, res[key]) + } + } + } + callback(metas) }) }, + editBoardMetas(room, id, prop, value) { redisClient.hget(`${REDIS_PREFIX}-room:${room}-board`, id, (err, res) => { const board = JSON.parse(res) diff --git a/server.js b/server.js index 35aaffe..44368bb 100644 --- a/server.js +++ b/server.js @@ -135,24 +135,27 @@ io.sockets.on('connection', (client) => { break case 'editBoardMetas': - clean_data = {} - // TODO: test if prop is in ['title', opacity, 'imageUrl'] - clean_data.id = message.data.id - clean_data.prop = message.data.prop - clean_data.value = scrub(message.data.value) + const id = scrub(message.data.id) + const prop = scrub(message.data.prop) + const value = scrub(message.data.value) + + const clean_data = { id, prop, value } - // send update to database getRoom(client, (room) => { - db.editBoardMetas(room, clean_data.id, clean_data.prop, clean_data.value) + const boardMeta = { + prop, + value + } + // Enregistre les metas dans Redis + db.createBoardMetas(room, id, boardMeta) }) - message_out = { + const message_out = { action: 'editBoardMetas', data: clean_data } broadcastToRoom(client, message_out) - break case 'moveCard': @@ -388,7 +391,9 @@ io.sockets.on('connection', (client) => { ************* */ function initClient(client) { // console.log ('initClient Started'); + getRoom(client, (room) => { + db.getAllCards(room, (cards) => { client.json.send({ action: 'initCards', @@ -428,6 +433,23 @@ function initClient(client) { } }) + db.getBoardMetas(room, (metas) => { + if (metas) { + for (const id in metas) { + const meta = metas[id] + client.json.send({ + action: 'editBoardMetas', + data: { + id, + prop: meta.prop, + value: meta.value + } + }) + } + } + }) + + roommates_clients = rooms.room_clients(room) roommates = [] diff --git a/views/index.jade b/views/index.jade index 558639d..e2360c5 100644 --- a/views/index.jade +++ b/views/index.jade @@ -48,9 +48,10 @@ block header path(d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872l-.1-.34zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z") block body - div(style="margin: 1em;") + div(style="margin: 1em;", data-html2canvas-ignore) label(for="opacity-range") Transparence du tableau : input#opacity-range(type="range", min="0", max="1", step="0.01", value="1") + input(type="button", name="screenshot", value="Prendre une capture d'écran", onclick="takescreenshot('body')") div.container div.board-container div.board-outline @@ -62,11 +63,3 @@ block body span(style="font-weight:bold; font-size:16px;") + div#delete-col.col-icon(title="Supprimer une colonne", style="width:20px; height:20px; display:flex; justify-content:center; align-items:center; border:1px solid #000; cursor:pointer; margin-top:4px;") span(style="font-weight:bold; font-size:16px;") − - - script. - document.addEventListener('DOMContentLoaded', function () { - const slider = document.getElementById('opacity-range'); - slider.addEventListener('input', function () { - document.documentElement.style.setProperty('--board-opacity', slider.value); - }); - }); diff --git a/views/layout.jade b/views/layout.jade index 5a10b16..797a0bd 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -11,7 +11,7 @@ html(lang="fr") body div#site-wrapper div#site-canvas - div#site-menu + div#site-menu(data-html2canvas-ignore) a.toggle-nav.pull-right.close-link(href="#") Fermer   svg(xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16") path(d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z") @@ -80,8 +80,8 @@ html(lang="fr") input(type="file")#import-input.form-control button#import-file.btn.btn-primary Importer div.credits Logiciel libre basé sur Framemo et Scrumblr - Code source - div#header-bar(data-url=locals.headerBarUrl) - header.container.main-header + div + header.container.main-header(data-html2canvas-ignore) div.title a(href="/", title="Retour à la page d'accueil") if locals.logoUrl @@ -107,4 +107,5 @@ html(lang="fr") script(src="lib/marked.min.js") script(src="lib/moment-with-locales.min.js") script(src="socket.io/socket.io.js") + script(src="html2canvas/dist/html2canvas.min.js") script(src="script.js")