feat: board opacity/background link with the room + start of screenshots feat

This commit is contained in:
leokontente 2025-06-05 14:31:53 +02:00
parent cf7fb776bd
commit 69a4b393f5
6 changed files with 106 additions and 28 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -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('<img src="images/ajax-loader.gif" width=43 height=11/>')
// 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)
});
}

View file

@ -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)

View file

@ -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 = []

View file

@ -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);
});
});

View file

@ -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&nbsp;&nbsp;
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 <a href="https://framagit.org/framasoft/framemo">Framemo</a> et <a href="https://github.com/aliasaria/scrumblr">Scrumblr</a> - <a href="https://framagit.org/colibris/framemo">Code source</a>
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")