diff --git a/src/main/kotlin/de/arindy/dicetower/IndexResource.kt b/src/main/kotlin/de/arindy/dicetower/IndexResource.kt index c30da0c..d4a21d6 100644 --- a/src/main/kotlin/de/arindy/dicetower/IndexResource.kt +++ b/src/main/kotlin/de/arindy/dicetower/IndexResource.kt @@ -1,23 +1,12 @@ package de.arindy.dicetower import io.quarkus.qute.TemplateInstance -import io.quarkus.runtime.annotations.RegisterForReflection import jakarta.enterprise.context.ApplicationScoped -import jakarta.ws.rs.Consumes import jakarta.ws.rs.GET -import jakarta.ws.rs.POST import jakarta.ws.rs.Path -import jakarta.ws.rs.PathParam import jakarta.ws.rs.Produces -import jakarta.ws.rs.QueryParam -import jakarta.ws.rs.core.Context import jakarta.ws.rs.core.MediaType -import jakarta.ws.rs.sse.OutboundSseEvent -import jakarta.ws.rs.sse.Sse -import jakarta.ws.rs.sse.SseBroadcaster -import jakarta.ws.rs.sse.SseEventSink import org.eclipse.microprofile.config.inject.ConfigProperty -import java.util.UUID @Path("/") @ApplicationScoped diff --git a/src/main/resources/META-INF/resources/app.js b/src/main/resources/META-INF/resources/app.js new file mode 100644 index 0000000..b9b0de0 --- /dev/null +++ b/src/main/resources/META-INF/resources/app.js @@ -0,0 +1,189 @@ +function addDice() { + let amount = +document.getElementById('dice-amount').innerText + document.getElementById('dice-amount').innerText = `${amount + 1}` +} + +function removeDice() { + let amount = +document.getElementById('dice-amount').innerText + if (amount > 1) { + document.getElementById('dice-amount').innerText = `${amount - 1}` + } +} + +function url() { + return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : ''); +} + +function start(event = undefined) { + if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) { + document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30'; + document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results'; + document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId'); + document.getElementById('resultFrame').src = document.getElementById('myResultsId').value; + document.getElementById('roll').hidden = false; + document.getElementById('start-container').hidden = true; + document.getElementById('options-container').hidden = false; + document.getElementById('dice-tower').hidden = false; + document.getElementById('name').hidden = true; + document.getElementById('room').hidden = true; + document.getElementById('how-to').hidden = true; + document.getElementById('results').hidden = false; + document.getElementById('all-results').hidden = !document.getElementById('gm').checked; + document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none'; + document.getElementById('nameH').innerHTML = '' + document.getElementById('name').value + ''; + document.getElementById('save-dice-hint-name').innerHTML = '' + document.getElementById('name').value + ''; + document.getElementById('roomLabel').hidden = true; + document.getElementById('nameLabel').hidden = true; + document.getElementById('nameH').hidden = false; + document.getElementById('room-hint').innerHTML = '

Room: ' + document.getElementById('room').value + '

'; + document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for ' + document.getElementById('name').value + ''; + document.title = document.getElementById('name').value + ' - Dice-Tower'; + + localStorage.setItem('last-name', document.getElementById('name').value) + localStorage.setItem('last-room', document.getElementById('room').value) + localStorage.setItem('last-gm', document.getElementById('gm').checked) + + if (localStorage.getItem(document.getElementById('name').value + "-theme")) { + document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme") + } + if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) { + document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor")); + } + + if (!localStorage.getItem(document.getElementById('name').value + '-started')) { + document.getElementById('urls-overlay').showPopover(); + } + + localStorage.setItem(document.getElementById('name').value + '-started', "true") + + let httpRequest = new XMLHttpRequest(); + httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register') + httpRequest.setRequestHeader('Content-Type', 'application/json') + httpRequest.send(JSON.stringify({ + name: document.getElementById('name').value, + overlay: document.getElementById('overlayId').value, + id: document.getElementById('room').value + ':' + localStorage.getItem('userId') + })) + if (document.getElementById('gm').checked) { + document.getElementById('resultSwitch').checked = true; + document.getElementById('resultFrame').src = document.getElementById('resultsId').value; + const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users'); + evtSource.addEventListener('message', function (event) { + let data = JSON.parse(event.data); + if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) { + let overlays = document.getElementById('overlay-urls'); + let newOverlay = document.getElementById(data.id) ?? document.createElement('div'); + newOverlay.replaceChildren(...[]); + newOverlay.id = data.id; + newOverlay.style.display = "flex"; + newOverlay.style.flexDirection = "row"; + newOverlay.style.justifyContent = "space-between"; + newOverlay.style.alignItems = "baseline"; + let newLabel = document.createElement('label'); + newLabel.for = data.id + 'url'; + newLabel.innerHTML = "Dice-Overlay for " + data.name + ""; + let newInput = document.getElementById('overlayId').cloneNode(); + newInput.type = "text"; + newInput.readOnly = true; + newInput.id = data.id + 'url'; + newInput.style.flexGrow = '1'; + newInput.value = data.overlay; + newInput.onclick = () => copyToClipboard(newInput.id) + newOverlay.appendChild(newLabel); + newOverlay.appendChild(newInput); + overlays.appendChild(newOverlay); + } + configurePopover(); + }); + } + } +} + +function rollEasy(dice) { + document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice; + roll(); +} + +function roll(event) { + if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) { + let httpRequest = new XMLHttpRequest(); + httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`)) + httpRequest.setRequestHeader('Content-Type', 'application/json') + httpRequest.send(JSON.stringify({ + name: document.getElementById('name').value, + command: document.getElementById('command').value, + themeColor: document.getElementById('themeColor').value, + theme: document.getElementById('theme').value + })) + } +} + +function saveDice() { + localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value) + localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value) +} + +function configurePopover() { + + const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']"); + popover.forEach((e) => { + + const target = document.querySelector("#" + e.getAttribute("popovertarget")); + e.addEventListener("mouseover", () => { + showSnackbar(target.innerHTML); + }); + e.addEventListener("mouseout", () => { + hideSnackbar(); + }); + + }); +} + +function copyToClipboard(id) { + let copyText = document.getElementById(id); + copyText.select(); + copyText.setSelectionRange(0, 99999); + navigator.clipboard.writeText(copyText.value).then(() => { + showSnackbar(" Link copied to clipboard:
" + copyText.value); + }) +} + +function showSnackbar(message) { + let snackbar = document.getElementById("snackbar"); + let snackbarContainer = document.getElementById("snackbar-container"); + snackbar.innerHTML = message; + snackbar.className = "show"; + snackbarContainer.className = "show"; +} + +function hideSnackbar() { + let snackbar = document.getElementById("snackbar"); + let snackbarContainer = document.getElementById("snackbar-container"); + snackbar.className = snackbar.className.replace("show", ""); + snackbarContainer.className = snackbarContainer.className.replace("show", ""); +} + +document.addEventListener("DOMContentLoaded", async () => { + if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) { + document.getElementById('name').value = localStorage.getItem('last-name'); + document.getElementById('room').value = localStorage.getItem('last-room'); + document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true'; + } + + document.getElementById('resultSwitch').addEventListener('change', function () { + if (!this.checked) { + document.getElementById('resultFrame').src = document.getElementById('myResultsId').value; + } else { + document.getElementById('resultFrame').src = document.getElementById('resultsId').value; + } + }) + + document.addEventListener("DOMContentLoaded", async () => { + if (!localStorage.getItem("userId")) { + localStorage.setItem("userId", self.crypto.randomUUID()); + } + }) + + configurePopover(); +}) + diff --git a/src/main/resources/META-INF/resources/dice-preview.js b/src/main/resources/META-INF/resources/dice-preview.js new file mode 100644 index 0000000..7fb28f9 --- /dev/null +++ b/src/main/resources/META-INF/resources/dice-preview.js @@ -0,0 +1,15 @@ +import DiceBox from "/vendor/dice-box/dice-box.es.js"; + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById('preview').onclick = async () => { + document.getElementById('dice-box').replaceChildren(...[]) + const diceBox = new DiceBox("#dice-box", { + assetPath: "/vendor/assets/", + theme: document.getElementById('theme').value, + themeColor: document.getElementById('themeColor').value, + scale: 14 + }); + await diceBox.init() + diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']); + } +}) diff --git a/src/main/resources/META-INF/resources/overlay/style.css b/src/main/resources/META-INF/resources/overlay/style.css new file mode 100644 index 0000000..250f390 --- /dev/null +++ b/src/main/resources/META-INF/resources/overlay/style.css @@ -0,0 +1,26 @@ +html, +body { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow: hidden; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + perspective: 1000px; +} + +#dice-box { + position: relative; + box-sizing: border-box; + width: 100%; + height: 100%; + background: transparent; + background-size: cover; +} + +#dice-box canvas { + width: 100%; + height: 100%; +} diff --git a/src/main/resources/META-INF/resources/style.css b/src/main/resources/META-INF/resources/style.css new file mode 100644 index 0000000..414c6b8 --- /dev/null +++ b/src/main/resources/META-INF/resources/style.css @@ -0,0 +1,209 @@ +.w3-theme-l6 { + color: #000 !important; + background-color: #999999 !important; + border-radius: 10px; +} + +.w3-theme-l4 { + color: #fff !important; + background-color: #666666 !important; + border-radius: 10px; +} + +.w3-theme-l1 { + color: #fff !important; + background-color: #333333 !important; + border-radius: 10px; +} + +button { + padding: 10px; + border: #333333 3px solid; + border-radius: 10px; + background: #333333; + color: #fff +} + +button:hover { + background: #444444; +} + +button:active { + background: #222222; +} + +input { + margin: 10px; +} + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 25px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 18px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #333333; +} + +input:focus + .slider { + box-shadow: 0 0 1px #333333; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +.tooltip { + position: fixed; + border-radius: 0.5rem; + padding: 15px; + color: #fff !important; + background-color: #333333dd !important +} + +#dice-box { + position: relative; + justify-self: center; + box-sizing: border-box; + width: 100%; + height: 100%; + background: transparent; + background-size: cover; +} + +#dice-box canvas { + width: 100%; + height: 100%; +} + +.checkbox { + position: relative; + padding-left: 50px; + margin-bottom: 12px; + margin-left: 10px; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.checkbox input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.checkmark { + margin-left: 25px; + position: absolute; + top: 0; + left: 0; + height: 15px; + width: 15px; + background-color: #eee; +} + +.checkbox:hover input ~ .checkmark { + background-color: #ccc; +} + +/* When the checkbox is checked, add a blue background */ +.checkbox input:checked ~ .checkmark { + background-color: #333333; +} + +/* Create the checkmark/indicator (hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the checkmark when checked */ +.checkbox input:checked ~ .checkmark:after { + display: block; +} + +/* Style the checkmark/indicator */ +.checkbox .checkmark:after { + left: 5px; + top: 2px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 3px 3px 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} + +#snackbar-container { + visibility: hidden; + position: fixed; + width: 100%; + z-index: 1; + bottom: 10%; + display: flex; + justify-content: center; + align-items: center; +} + +#snackbar { + visibility: hidden; + text-align: center; + border-radius: 0.5rem; + padding: 15px; + color: #fff !important; + border: #fff 5px solid; + background-color: #333333dd !important +} + +#snackbar-container.show { + visibility: visible; +} + +#snackbar.show { + visibility: visible; + -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; + animation: fadein 0.5s, fadeout 0.5s 2.5s; +} diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 3dda786..1e3d7d6 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -6,234 +6,11 @@ + - + +
.. they see them rolling
- - diff --git a/src/main/resources/templates/overlay.html b/src/main/resources/templates/overlay.html index 6f7dec2..3305177 100644 --- a/src/main/resources/templates/overlay.html +++ b/src/main/resources/templates/overlay.html @@ -4,35 +4,7 @@ Dice-Tower - Overlay - +
@@ -40,7 +12,9 @@ function url() { return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : ''); } + import DiceBox from "/vendor/dice-box/dice-box.es.js"; + const evtSource = new EventSource(url() + "/dice/{diceid??}/stream"); const diceBox = new DiceBox("#dice-box", { assetPath: "/vendor/assets/", @@ -59,7 +33,7 @@ scale: {scale} }); - document.addEventListener("DOMContentLoaded", async() => { + document.addEventListener("DOMContentLoaded", async () => { await diceBox.init() let timeout = 0; @@ -68,20 +42,27 @@ let data = JSON.parse(event.data); diceBox.onRollComplete = (rollResult) => { let httpRequest = new XMLHttpRequest(); - httpRequest.open('POST',url() + '/dice/' + data.room + '/results') + httpRequest.open('POST', url() + '/dice/' + data.room + '/results') httpRequest.setRequestHeader('Content-Type', 'application/json') httpRequest.send(JSON.stringify({ name: data.name, user: data.user, themeColor: data.themeColor, results: rollResult, - } )) - if ({clearAfter} > 0) { - timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000) + })) + if ({clearAfter} > + 0 + ) + { + timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000 + ) } } - diceBox.roll(data.roll, { theme: data.theme?.length > 0 ? data.theme : 'default', themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' }); + diceBox.roll(data.roll, { + theme: data.theme?.length > 0 ? data.theme : 'default', + themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' + }); }) }) diff --git a/src/main/resources/templates/results.html b/src/main/resources/templates/results.html index f3d3b13..941120d 100644 --- a/src/main/resources/templates/results.html +++ b/src/main/resources/templates/results.html @@ -8,12 +8,12 @@ -
-
+