From 55eebe260311d955e94eb41291b1bdcacc63fa14 Mon Sep 17 00:00:00 2001 From: Marc Michalsky Date: Fri, 1 Sep 2023 22:54:04 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Initial=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/html.iml | 8 + .idea/inspectionProfiles/Project_Default.xml | 26 +++ .idea/modules.xml | 8 + .idea/php.xml | 19 ++ .idea/vcs.xml | 6 + .idea/workspace.xml | 129 ++++++++++++ LICENSE | 9 + README.md | 9 + index.html | 33 ++++ resources/images/favicon.png | Bin 0 -> 530 bytes resources/sounds/notification.ogg | Bin 0 -> 14171 bytes script.js | 198 +++++++++++++++++++ style.css | 186 +++++++++++++++++ 13 files changed, 631 insertions(+) create mode 100644 .idea/html.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 LICENSE create mode 100644 README.md create mode 100755 index.html create mode 100644 resources/images/favicon.png create mode 100644 resources/sounds/notification.ogg create mode 100644 script.js create mode 100644 style.css diff --git a/.idea/html.iml b/.idea/html.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/html.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..44fc506 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,26 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e5e05d2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..f324872 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..d58740c --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + { + "customColor": "", + "associatedIndex": 8 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1677581124694 + + + + + + + + + + + + + + + + + + + + + + + +

The name field contains invalid characters.

+ + + + + diff --git a/resources/images/favicon.png b/resources/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..107edcf4bd2ee013db92a170eb7e1afcc0e69acd GIT binary patch literal 530 zcmV+t0`2{YP)CG{r~?1AbT$Z)Bl-_Mq4f-Yhi?O{vWgCWMCDVz`)3q z0u*CHHyFzQ$MFB(9ERWT3K*@9e+P+y4FGFq7v9eBpV1zNMpR+O{|6a+!f~-9zXxcP@QAPz{<)D*8KbT zUxwBxhZtlf`5BTUjF9XC8Gz&kkj>dOTN(cT`_E97ZOb4pBLG(X@W}^;wyB31n3)(E zrnYz^TnF|7QXqm9Pndh0;n~a23{AyO@PPdB^AAH^?>+`8V5}4*TOvgzC=k)2@$cV% z41a)_oeeb>e*XHyz{1SLzzoDF(Fl$Ww1h-djKC5S*Z@kCGTabyGZRW0ATc`w05|m5 U?{1oB#Q*>R07*qoM6N<$f<)`|(f|Me literal 0 HcmV?d00001 diff --git a/resources/sounds/notification.ogg b/resources/sounds/notification.ogg new file mode 100644 index 0000000000000000000000000000000000000000..45b5eb23b029f5c1562e55fce5c19d4800d2e410 GIT binary patch literal 14171 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 literal 0 HcmV?d00001 diff --git a/script.js b/script.js new file mode 100644 index 0000000..a101de5 --- /dev/null +++ b/script.js @@ -0,0 +1,198 @@ +// Enable debug mode if the URL contains the debug parameter +let debug = false; +if (window.location.href.includes('debug')) { + console.log('Debug mode enabled'); + debug = true; +} + +// Initialize the EventSource and the HTML elements +const eventSource = new EventSource('https://ntfy.example.org/guestbook/sse?since=all'); +const entryText = document.getElementById('entryText'); +const name = document.getElementById('name'); +const submitEntry = document.getElementById('submitEntry'); +const entryTable = document.getElementById('entryTable'); +const errorMessage = document.getElementById('error-message'); +const bell = new Audio('/resources/sounds/notification.ogg'); + +// Disable the notification sound for the first 2 seconds until the guestbook is loaded initially +let bellEnabled = false; + +// Event listener for the Enter key +function handleKeydown(event) { + if (event.key === 'Enter') { + sendMessage(); + } +} + +// Check if the name is valid +function checkName(name) { + if (name.match(/:\s/)) { + errorMessage.classList.add('visible'); + console.error('Invalid name'); + return false; + } + return true; +} + +// Send new entry to the ntfy server +function sendMessage() { + const text = entryText.value.trim(); + const author = name.value.trim() ? name.value.trim() : 'Anonymous'; + if (checkName(author)) { + errorMessage.classList.remove('visible'); + const message = author + ': ' + text; + if (text.length > 0) { + fetch('https://ntfy.example.org/guestbook', { + method: 'POST', + body: message + }) + .then(() => { + // Empty the input field + entryText.value = ''; + // Save the name in the local storage + localStorage.setItem('name', author); + }) + .catch(error => console.error(error)); + } + } +} + +// Calculate time passed since the message was sent +function timeSince(date) { + let seconds = Math.floor(Date.now() / 1000 - date); + let interval = Math.floor(seconds / 3600); + if (interval >= 2) { + return interval + " hours ago"; + } + if (interval === 1) { + return "1 hour ago" + } + interval = Math.floor(seconds / 60); + if (interval > 1) { + return interval + " minutes ago"; + } + if (interval === 1) { + return "1 minute ago" + } + if (seconds > 10) { + return Math.floor(seconds) + " seconds ago"; + } + if (seconds > -10) { + return "just now" + } + if (seconds > -60) { + return "in " + Math.floor(-seconds) + " seconds"; + } + interval = Math.floor(-seconds / 60); + if (interval === 1) { + return "in 1 minute"; + } + if (interval < 60) { + return "in " + interval + " minutes"; + } + interval = Math.floor(-seconds / 3600); + if (interval === 1) { + return "in 1 hour"; + } + if (interval > 1) { + return "in " + interval + " hours"; + } +} + +// Parse the time from the message +function parseTimeString(data) { + return ` | ${timeSince(data.time)}, expires in ${timeSince(data.expires)}` +} + +// Update time +function updateTime() { + const timeElements = document.getElementsByClassName('time'); + for (let i = 0; i < timeElements.length; i++) { + const time = timeElements[i]; + time.textContent = parseTimeString(time.dataset); + } +} + +// Add a new entry to the list +function addEntry(data) { + const row = document.createElement('tr'); + + // Create a new cell for the author + const authorCell = document.createElement('td'); + authorCell.classList.add('author'); + authorCell.style.fontWeight = 'italic'; + + // Create a new cell for the message + const messageCell = document.createElement('td'); + messageCell.classList.add('message'); + + // Split the message into author and text + const [name, message] = data.message.split(/:\s(.*)/); + + // Create span with the time that passed since the message was sent + // and when the message will expire + const time = document.createElement('span'); + time.classList.add('time'); + time.dataset.time = data.time; + time.dataset.expires = data.expires; + time.textContent = parseTimeString(data); + + // Set the cell content + authorCell.textContent = name + ':'; + messageCell.innerHTML = message + time.outerHTML; + + // Append the cells to the row and the row to the table + row.appendChild(authorCell); + row.appendChild(messageCell); + + entryTable.appendChild(row); +} + +// Connect to the server to receive new entries +function streamEntries() { + eventSource.onmessage = (m) => { + if (debug) { + console.log(m.data); + } + const data = JSON.parse(m.data) + addEntry(data); + + // Play a notification sound for new entries + try { + if (bellEnabled) { + bell.play(); + } + } catch (DOMException) { + console.log('The notification sound was blocked by the browser'); + } + }; +} + +// Initialize the page +function setup() { + // Add event listeners + submitEntry.addEventListener('click', sendMessage); + entryText.addEventListener('keydown', handleKeydown); + + // Start streaming the entries + streamEntries(); + + // Update the time values every 10 seconds + setInterval(updateTime, 10000); + + // Load the name from the local storage + name.value = localStorage.getItem('name'); + + // Enable the notification sound after 2 seconds + setTimeout(() => { + bellEnabled = true; + }, 2000); + + // Close the connection when the page is closed + window.addEventListener('beforeunload', function (e) { + e.preventDefault(); + eventSource.close(); + }); +} + +window.addEventListener('load', setup) diff --git a/style.css b/style.css new file mode 100644 index 0000000..fdb5d3a --- /dev/null +++ b/style.css @@ -0,0 +1,186 @@ +body { + background-color: #01252b; + color: white; + font-family: monospace; +} + +h1 span { + display: inline-block; + line-height: 36px; +} + +#container { + position: absolute; + max-width: 1000px; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 84%; + height: auto; + margin: auto; + padding: 8% 4%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +#hi { + display: block; + text-align: center; +} + +#rotate { + -ms-transition: 0.4s ease-in-out; + -webkit-transition: 0.4s ease-in-out; + transition: 0.4s ease-in-out; +} + +#rotate:hover, #rotate:active { + -ms-transform: rotate(180deg); /* IE 9 */ + -webkit-transform: rotate(180deg); /* Chrome, Safari, Opera */ + transform: rotate(180deg); +} + +.guestbook { + display: block; + width: 100%; + height: 100%; + min-height: 168px; + background: darkslategray; + margin: 20px auto; + box-shadow: 10px 10px 5px #162222; + border-radius: 10px; + overflow: auto; +} + +.guestbook h2 { + text-align: center; +} + +.guestbook table { + margin: 0 8px; + border: none; +} + +.guestbook table td { + padding: 8px; + text-align: left; + vertical-align: top; + white-space: preserve; +} + +.author { + font-weight: bold; + width: 110px; +} + +.guestbook-form { + display: flex; + flex-direction: row; + width: 100%; + text-align: center; + align-items: center; + margin: 10px auto; + box-shadow: 10px 10px 5px #162222; +} + +.guestbook-form input { + display: block; + float: left; + width: 90%; + resize: none; + padding: 10px; + background: darkslategray; + color: #ffffff; + border: none; + border-radius: 6px 0 0 6px; +} + +.guestbook-form #name { + float: left; + width: 120px; + border-radius: 6px; + margin-right: 8px; + font-weight: bold; +} + +.guestbook-form #entryText { + width: 80%; +} + +.guestbook-form button { + float: right; + width: 10%; + min-width: 72px; + height: auto; + padding: 10px; + border-radius: 0 6px 6px 0; + background: #2e6971;; + color: #ffffff; + border: none; + font-weight: bold; + cursor: pointer; +} + +.time { + display: inline; + opacity: 0; + color: #61959d; + font-size: small; + transition: opacity 0.5s linear; +} + +tr:hover .time { + opacity: 1; +} + +#error-message { + opacity: 0; + color: red; + display: block; + text-align: center; + margin: 10px 0; + transition: opacity 0.5s linear; +} + +#error-message.visible { + opacity: 1; +} + +@media (max-width: 640px) { + + .guestbook-form { + display: block; + box-shadow: none; + width: 100%; + padding: 0; + align-items: center; + } + + .guestbook-form input { + border-radius: 6px; + margin-bottom: 12px; + box-shadow: 10px 10px 5px #162222; + } + + .guestbook-form #name { + width: calc(100% - 20px); + margin-right: 0; + } + + .guestbook-form #entryText { + width: calc(100% - 20px); + } + + .guestbook-form button { + width: 100%; + border-radius: 6px; + box-shadow: 10px 10px 5px #162222; + } + + .time { + display: none; + } +}