From 5553a8af014119ce1e00cc5022e9a0acb6c53011 Mon Sep 17 00:00:00 2001 From: Jocadbz Date: Fri, 20 Feb 2026 13:16:31 -0300 Subject: [PATCH] move inline css and js into proper files --- handlers/app.go | 2 + handlers/chat.go | 2 + static/chat.js | 564 +++++++++++++++++++++ static/style.css | 435 ++++++++++++++++- templates/base.html | 7 +- templates/pages/chat.html | 999 +++----------------------------------- 6 files changed, 1062 insertions(+), 947 deletions(-) create mode 100644 static/chat.js diff --git a/handlers/app.go b/handlers/app.go index 69abd92..ffc0881 100644 --- a/handlers/app.go +++ b/handlers/app.go @@ -18,6 +18,8 @@ type PageData struct { BasePath string StaticPath string CurrentURL string + ContentTemplate string + BodyClass string } type Config struct { diff --git a/handlers/chat.go b/handlers/chat.go index 5f2bda3..5314cd7 100644 --- a/handlers/chat.go +++ b/handlers/chat.go @@ -238,6 +238,8 @@ func ChatHandler(app *App) http.HandlerFunc { BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", CurrentURL: r.URL.Path, + ContentTemplate: "chat-content", + BodyClass: "chat-page", }, Board: *board, Messages: messages, diff --git a/static/chat.js b/static/chat.js new file mode 100644 index 0000000..7bdba72 --- /dev/null +++ b/static/chat.js @@ -0,0 +1,564 @@ +(() => { + 'use strict'; + + const chatContainer = document.querySelector('.chat-container'); + if (!chatContainer) { + return; + } + + const boardId = chatContainer.dataset.boardId; + const basePath = chatContainer.dataset.basePath || ''; + const currentUsername = chatContainer.dataset.currentUsername || ''; + const usernamesScript = document.getElementById('chat-usernames'); + let allUsernames = []; + if (usernamesScript) { + try { + allUsernames = JSON.parse(usernamesScript.textContent || '[]'); + } catch (err) { + console.error('Failed to parse chat usernames:', err); + allUsernames = []; + } + } + + let ws; + let autocompleteActive = false; + let replyToId = -1; + let reconnectAttempts = 0; + let reconnectTimeout; + let isUserAtBottom = true; + let unreadCount = 0; + const maxReconnectDelay = 30000; + const typingUsers = new Set(); + + function updateConnectionStatus(status) { + const dot = document.getElementById('connection-dot'); + const text = document.getElementById('connection-text'); + if (!dot || !text) { + return; + } + dot.className = 'connection-dot ' + status; + + if (status === 'connected') { + text.textContent = 'Connected'; + reconnectAttempts = 0; + } else if (status === 'connecting') { + text.textContent = 'Connecting...'; + } else if (status === 'disconnected') { + text.textContent = 'Disconnected'; + } + } + + function connectWebSocket() { + if (!boardId) { + return; + } + updateConnectionStatus('connecting'); + + ws = new WebSocket('ws://' + window.location.host + basePath + '/chat/?ws=true&id=' + boardId); + + ws.onopen = function() { + updateConnectionStatus('connected'); + reconnectAttempts = 0; + }; + + ws.onmessage = function(event) { + const msg = JSON.parse(event.data); + + if (msg.type === 'typing') { + showTypingIndicator(msg.username); + return; + } + + appendMessage(msg); + }; + + ws.onclose = function() { + updateConnectionStatus('disconnected'); + console.log('WebSocket closed, reconnecting...'); + scheduleReconnect(); + }; + + ws.onerror = function(error) { + console.error('WebSocket error:', error); + updateConnectionStatus('disconnected'); + }; + } + + function scheduleReconnect() { + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + } + + const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), maxReconnectDelay); + reconnectAttempts++; + + console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})...`); + reconnectTimeout = setTimeout(connectWebSocket, delay); + } + + function sendMessage() { + const input = document.getElementById('chat-input-text'); + if (!input) { + return; + } + const content = input.value.trim(); + if (content === '') { + return; + } + + const msg = { + type: 'message', + content: content, + replyTo: replyToId + }; + + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify(msg)); + input.value = ''; + cancelReply(); + } else { + console.error('WebSocket is not open. Current state:', ws ? ws.readyState : 'undefined'); + alert('Cannot send message: Not connected to chat server'); + } + } + + function sendTypingIndicator() { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: 'typing', username: currentUsername })); + } + } + + function showTypingIndicator(username) { + if (username === currentUsername) { + return; + } + + typingUsers.add(username); + updateTypingDisplay(); + + setTimeout(() => { + typingUsers.delete(username); + updateTypingDisplay(); + }, 3000); + } + + function updateTypingDisplay() { + const indicator = document.getElementById('typing-indicator'); + const usersSpan = document.getElementById('typing-users'); + + if (!indicator || !usersSpan) { + return; + } + + if (typingUsers.size === 0) { + indicator.classList.remove('visible'); + } else { + const users = Array.from(typingUsers); + if (users.length === 1) { + usersSpan.textContent = `${users[0]} is typing`; + } else if (users.length === 2) { + usersSpan.textContent = `${users[0]} and ${users[1]} are typing`; + } else { + usersSpan.textContent = `${users[0]}, ${users[1]}, and ${users.length - 2} other${users.length - 2 > 1 ? 's' : ''} are typing`; + } + indicator.classList.add('visible'); + } + } + + function appendMessage(msg) { + const messages = document.getElementById('chat-messages'); + if (!messages) { + return; + } + + if (document.getElementById('msg-' + msg.id)) { + return; + } + + const wasAtBottom = isUserAtBottom; + + const msgDiv = document.createElement('div'); + let highlightClass = ''; + if (msg.mentions && msg.mentions.includes(currentUsername)) { + highlightClass = ' chat-message-highlighted'; + } + msgDiv.className = 'chat-message' + highlightClass; + msgDiv.id = 'msg-' + msg.id; + msgDiv.dataset.user = msg.username; + msgDiv.dataset.reply = msg.replyTo || 0; + + let pfpHTML = '
'; + if (msg.pfpFileId && msg.pfpFileId.Valid) { + pfpHTML = `PFP`; + } + const replyHTML = msg.replyTo > 0 ? `
Replying to message...
` : ''; + + msgDiv.innerHTML = ` +
+ ${pfpHTML} + ${msg.username} + ${new Date(msg.timestamp).toLocaleString()} +
+ ${replyHTML} +
${msg.content}
+
+ Reply +
+ `; + messages.appendChild(msgDiv); + + applyGrouping(); + + if (wasAtBottom) { + messages.scrollTop = messages.scrollHeight; + unreadCount = 0; + updateUnreadBadge(); + } else if (msg.username !== currentUsername) { + unreadCount++; + updateUnreadBadge(); + } + } + + function updateUnreadBadge() { + const badge = document.getElementById('unread-badge'); + if (!badge) { + return; + } + if (unreadCount > 0) { + badge.textContent = unreadCount > 99 ? '99+' : unreadCount; + badge.style.display = 'flex'; + } else { + badge.style.display = 'none'; + } + } + + function jumpToBottom() { + const messages = document.getElementById('chat-messages'); + if (!messages) { + return; + } + messages.scrollTo({ + top: messages.scrollHeight, + behavior: 'smooth' + }); + unreadCount = 0; + updateUnreadBadge(); + } + + function checkScrollPosition() { + const messages = document.getElementById('chat-messages'); + const jumpButton = document.getElementById('jump-to-bottom'); + if (!messages || !jumpButton) { + return; + } + const threshold = 100; + + isUserAtBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < threshold; + + if (isUserAtBottom) { + jumpButton.classList.remove('visible'); + unreadCount = 0; + updateUnreadBadge(); + } else { + jumpButton.classList.add('visible'); + } + } + + function replyToMessage(id, username) { + replyToId = id; + const replyIndicator = document.getElementById('reply-indicator'); + const replyUsernameSpan = document.getElementById('reply-username'); + if (!replyIndicator || !replyUsernameSpan) { + return; + } + replyUsernameSpan.textContent = `Replying to ${username}`; + replyIndicator.style.display = 'flex'; + const input = document.getElementById('chat-input-text'); + if (input) { + input.focus(); + } + } + + function cancelReply() { + replyToId = -1; + const replyIndicator = document.getElementById('reply-indicator'); + if (replyIndicator) { + replyIndicator.style.display = 'none'; + } + } + + function scrollToMessage(id) { + const msgElement = document.getElementById('msg-' + id); + if (msgElement) { + msgElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); + msgElement.style.transition = 'background-color 0.5s'; + msgElement.style.backgroundColor = '#f582ae'; + setTimeout(() => { + msgElement.style.backgroundColor = ''; + }, 1000); + } + } + + function showAutocompletePopup(suggestions, x, y) { + const popup = document.getElementById('autocomplete-popup'); + if (!popup) { + return; + } + popup.innerHTML = ''; + popup.style.position = 'fixed'; + popup.style.left = x + 'px'; + popup.style.top = y + 'px'; + popup.style.display = 'block'; + autocompleteActive = true; + suggestions.forEach(username => { + const item = document.createElement('div'); + item.className = 'autocomplete-item'; + item.textContent = username; + item.onmousedown = (e) => { + e.preventDefault(); + completeMention(username); + popup.style.display = 'none'; + autocompleteActive = false; + }; + popup.appendChild(item); + }); + } + + function completeMention(username) { + const input = document.getElementById('chat-input-text'); + if (!input) { + return; + } + const text = input.value; + const caretPos = input.selectionStart; + const textBeforeCaret = text.substring(0, caretPos); + const atIndex = textBeforeCaret.lastIndexOf('@'); + + if (atIndex !== -1) { + const prefix = text.substring(0, atIndex); + const suffix = text.substring(caretPos); + input.value = prefix + '@' + username + ' ' + suffix; + const newCaretPos = prefix.length + 1 + username.length + 1; + input.focus(); + input.setSelectionRange(newCaretPos, newCaretPos); + } + } + + function getCaretCoordinates(element, position) { + const mirrorDivId = 'input-mirror-div'; + const div = document.createElement('div'); + div.id = mirrorDivId; + document.body.appendChild(div); + const style = window.getComputedStyle(element); + const properties = ['border', 'boxSizing', 'fontFamily', 'fontSize', 'fontWeight', 'letterSpacing', 'lineHeight', 'padding', 'textDecoration', 'textIndent', 'textTransform', 'whiteSpace', 'wordSpacing', 'wordWrap', 'width']; + properties.forEach(prop => { + div.style[prop] = style[prop]; + }); + div.style.position = 'absolute'; + div.style.top = '-9999px'; + div.style.left = '0px'; + div.textContent = element.value.substring(0, position); + + const span = document.createElement('span'); + span.textContent = element.value.substring(position) || '.'; + div.appendChild(span); + + const coords = { top: span.offsetTop, left: span.offsetLeft }; + document.body.removeChild(div); + return coords; + } + + function applyGrouping() { + const container = document.getElementById('chat-messages'); + if (!container) { + return; + } + const msgs = Array.from(container.querySelectorAll('.chat-message')).filter(el => el.id.startsWith('msg-')); + + for (let i = 0; i < msgs.length; i++) { + const curr = msgs[i]; + + curr.classList.remove('grouped'); + + if (i === 0) { + continue; + } + + const prev = msgs[i - 1]; + const currUser = curr.dataset.user; + const prevUser = prev.dataset.user; + const currReply = parseInt(curr.dataset.reply, 10) || -1; + + if (currUser === prevUser && (currReply <= 0)) { + curr.classList.add('grouped'); + } + } + } + + function handleAutocompleteInput(e) { + const input = e.target; + const text = input.value; + const caretPos = input.selectionStart; + const popup = document.getElementById('autocomplete-popup'); + if (!popup) { + return; + } + const textBeforeCaret = text.substring(0, caretPos); + const atIndex = textBeforeCaret.lastIndexOf('@'); + + if (atIndex !== -1 && (atIndex === 0 || /\s/.test(text.charAt(atIndex - 1)))) { + const query = textBeforeCaret.substring(atIndex + 1); + if (!/\s/.test(query)) { + const suggestions = allUsernames.filter(u => u.toLowerCase().startsWith(query.toLowerCase())).slice(0, 10); + if (suggestions.length > 0 && query.length > 0) { + const coords = getCaretCoordinates(input, atIndex); + const rect = input.getBoundingClientRect(); + showAutocompletePopup(suggestions, rect.left + coords.left, rect.top + coords.top + 20); + } else { + popup.style.display = 'none'; + autocompleteActive = false; + } + return; + } + } + popup.style.display = 'none'; + autocompleteActive = false; + } + + function handleAutocompleteBlur() { + setTimeout(() => { + if (!document.querySelector('.autocomplete-popup:hover')) { + const popup = document.getElementById('autocomplete-popup'); + if (popup) { + popup.style.display = 'none'; + } + autocompleteActive = false; + } + }, 150); + } + + function handleMessageKeydown(e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + } + + function setupDraftAutoSave(chatInput) { + if (!chatInput) { + return; + } + const draftKey = `draft_chat_${boardId}`; + let draftSaveTimeout; + + const existingDraft = loadDraft(draftKey); + const draftTimestamp = getDraftTimestamp(draftKey); + if (existingDraft && draftTimestamp) { + const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000); + if (draftTimestamp > sevenDaysAgo) { + const indicator = showDraftIndicator( + existingDraft, + draftTimestamp, + (content) => { + chatInput.value = content; + chatInput.focus(); + }, + () => { + clearDraft(draftKey); + } + ); + const chatInputContainer = document.querySelector('.chat-input'); + if (chatInputContainer && chatInputContainer.parentNode) { + chatInputContainer.parentNode.insertBefore(indicator, chatInputContainer); + } + } else { + clearDraft(draftKey); + } + } + + chatInput.addEventListener('input', () => { + clearTimeout(draftSaveTimeout); + draftSaveTimeout = setTimeout(() => { + const content = chatInput.value.trim(); + if (content) { + saveDraft(draftKey, content); + } else { + clearDraft(draftKey); + } + }, 2000); + }); + } + + function setupTypingIndicator(chatInput) { + if (!chatInput) { + return; + } + let lastTypingTime = 0; + chatInput.addEventListener('input', () => { + const now = Date.now(); + if (now - lastTypingTime > 2000) { + sendTypingIndicator(); + lastTypingTime = now; + } + }); + } + + function setupSendMessageDraftClear(chatInput) { + if (!chatInput) { + return; + } + const draftKey = `draft_chat_${boardId}`; + const originalSendMessage = window.sendMessage; + window.sendMessage = function() { + const content = chatInput.value.trim(); + if (content !== '') { + clearDraft(draftKey); + } + if (typeof originalSendMessage === 'function') { + originalSendMessage(); + } else { + sendMessage(); + } + }; + } + + function initChat() { + connectWebSocket(); + + const messagesContainer = document.getElementById('chat-messages'); + if (messagesContainer) { + messagesContainer.scrollTop = messagesContainer.scrollHeight; + applyGrouping(); + messagesContainer.addEventListener('scroll', checkScrollPosition); + } + + const jumpButton = document.getElementById('jump-to-bottom'); + if (jumpButton) { + jumpButton.addEventListener('click', jumpToBottom); + } + + const chatInput = document.getElementById('chat-input-text'); + if (chatInput) { + chatInput.addEventListener('input', handleAutocompleteInput); + chatInput.addEventListener('blur', handleAutocompleteBlur); + chatInput.addEventListener('keydown', handleMessageKeydown); + } + + setupTypingIndicator(chatInput); + setupDraftAutoSave(chatInput); + setupSendMessageDraftClear(chatInput); + + checkScrollPosition(); + } + + window.sendMessage = sendMessage; + window.replyToMessage = replyToMessage; + window.cancelReply = cancelReply; + window.scrollToMessage = scrollToMessage; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initChat); + } else { + initChat(); + } +})(); diff --git a/static/style.css b/static/style.css index 3d1cf89..0e2a857 100644 --- a/static/style.css +++ b/static/style.css @@ -6,6 +6,11 @@ body { color: #001858; /* blue */ } +body.chat-page { + height: 100vh; + overflow: hidden; +} + main { display: flex; flex-direction: column; @@ -13,6 +18,12 @@ main { padding: 25px; } +body.chat-page main { + padding: 0; + margin-top: 3em; + height: calc(100vh - 3em); +} + main > header { text-align: center; margin-bottom: 1em; @@ -343,6 +354,344 @@ p.thread-info { background-color: #ffe0f0; /* Light pink background */ animation: highlight-fade 2s ease-out; } + +/* Chat page styles */ +.chat-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + border: none; + border-radius: 0; + background-color: #fef6e4; + box-shadow: none; +} + +.chat-header { + padding: 10px; + text-align: center; + border-bottom: 1px solid #001858; + position: relative; +} + +.connection-status { + position: absolute; + top: 10px; + right: 10px; + display: flex; + align-items: center; + gap: 6px; + font-size: 0.85em; + padding: 4px 10px; + border-radius: 12px; + background-color: rgba(0,0,0,0.1); +} + +.connection-dot { + width: 10px; + height: 10px; + border-radius: 50%; + animation: pulse 2s ease-in-out infinite; +} + +.connection-dot.connected { + background-color: #4ade80; +} + +.connection-dot.connecting { + background-color: #fbbf24; +} + +.connection-dot.disconnected { + background-color: #f87171; + animation: none; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.typing-indicator { + padding: 8px 12px; + background-color: #f3d2c1; + border-radius: 5px; + font-size: 0.85em; + font-style: italic; + color: #001858; + margin-bottom: 8px; + display: none; +} + +.typing-indicator.visible { + display: block; +} + +.typing-dots { + display: inline-block; + margin-left: 4px; +} + +.typing-dots span { + animation: typing-blink 1.4s infinite; + display: inline-block; +} + +.typing-dots span:nth-child(2) { + animation-delay: 0.2s; +} + +.typing-dots span:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes typing-blink { + 0%, 60%, 100% { opacity: 0.3; } + 30% { opacity: 1; } +} + +.jump-to-bottom { + position: absolute; + bottom: 70px; + right: 20px; + background-color: #001858; + color: #fef6e4; + border: 2px solid #001858; + border-radius: 50%; + width: 45px; + height: 45px; + cursor: pointer; + display: none; + align-items: center; + justify-content: center; + font-size: 1.3em; + box-shadow: 0px 4px 12px rgba(0,0,0,0.3); + transition: transform 0.2s ease; + z-index: 100; +} + +.jump-to-bottom.visible { + display: flex; +} + +.jump-to-bottom:hover { + transform: translateY(2px); + background-color: #8bd3dd; +} + +.jump-to-bottom .unread-badge { + position: absolute; + top: -5px; + right: -5px; + background-color: #f582ae; + color: #fef6e4; + border-radius: 50%; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.7em; + font-weight: bold; +} + +.message-status { + font-size: 0.7em; + color: #666; + margin-left: 8px; +} + +.message-status.sending { + color: #fbbf24; +} + +.message-status.sent { + color: #4ade80; +} + +.message-status.failed { + color: #f87171; + cursor: pointer; +} + +.chat-breadcrumb { + background-color: #f3d2c1; + padding: 8px 12px; + border-bottom: 1px solid #001858; + font-size: 0.85em; + text-align: left; +} + +.chat-breadcrumb a { + color: #001858; + text-decoration: none; +} + +.chat-breadcrumb a:hover { + color: #f582ae; + text-decoration: underline; +} + +.chat-breadcrumb-separator { + margin: 0 6px; + opacity: 0.6; +} + +.chat-messages { + flex: 1; + overflow-y: auto; + padding: 8px; + display: flex; + flex-direction: column; +} + +.chat-message { + margin-bottom: 8px; + max-width: 90%; + position: relative; +} + +.chat-message-header { + display: flex; + align-items: center; + margin-bottom: 3px; +} + +.chat-message-pfp { + width: 30px; + height: 30px; + border-radius: 50%; + margin-right: 8px; +} + +.chat-message-username { + font-weight: bold; + color: #001858; + font-size: 0.9em; +} + +.chat-message-timestamp { + font-size: 0.7em; + color: #666; + margin-left: 8px; +} + +.chat-message-content { + background-color: #f3d2c1; + padding: 6px 10px; + border-radius: 5px; + line-height: 1.3; + font-size: 0.9em; + word-wrap: break-word; + overflow-wrap: break-word; + word-break: break-word; +} + +.chat-message-reply { + background-color: rgba(0,0,0,0.1); + padding: 4px 8px; + border-radius: 5px; + margin-bottom: 3px; + font-size: 0.8em; + cursor: pointer; +} + +.chat-message-mention { + color: #f582ae; + font-weight: bold; +} + +.chat-input { + padding: 8px; + border-top: 1px solid #001858; + display: flex; + flex-direction: column; +} + +.chat-input textarea { + resize: none; + height: 50px; + margin-bottom: 8px; + font-size: 0.9em; +} + +.chat-input button { + align-self: flex-end; + width: auto; + padding: 6px 12px; + font-size: 0.9em; +} + +.chat-message:hover .post-actions { + opacity: 1; +} + +.post-actions a { + color: #001858; + text-decoration: none; + font-size: 0.8em; + padding: 2px 5px; + border: 1px solid #001858; + border-radius: 3px; +} + +.post-actions a:hover { + background-color: #8bd3dd; + color: #fef6e4; +} + +.autocomplete-popup { + position: absolute; + background-color: #fff; + border: 1px solid #001858; + border-radius: 5px; + max-height: 200px; + overflow-y: auto; + box-shadow: 0px 4px 8px rgba(0,0,0,0.2); + z-index: 1000; + display: none; +} + +.autocomplete-item { + padding: 6px 10px; + cursor: pointer; + font-size: 0.9em; +} + +.autocomplete-item:hover { + background-color: #f3d2c1; +} + +.reply-indicator { + background-color: #001858; + color: #fef6e4; + padding: 5px 10px; + border-radius: 5px; + margin-bottom: 8px; + display: none; + align-items: center; + justify-content: space-between; +} + +.reply-indicator span { + font-size: 0.9em; +} + +.reply-indicator button { + background: none; + border: none; + color: #fef6e4; + cursor: pointer; + font-size: 0.9em; + padding: 0 5px; + margin: 0; + width: auto; +} + +.reply-indicator button:hover { + background: none; + color: #f582ae; +} /* Grouped messages (hide header for consecutive messages from same user) */ .chat-message.grouped { margin-top: -4px; @@ -440,6 +789,90 @@ p.thread-info { border-color: #f582ae; } } + + body.chat-page { + background-color: #333; + } + + .chat-container { + background-color: #444; + } + + .chat-breadcrumb { + background-color: #555; + } + + .chat-breadcrumb a { + color: #fef6e4; + } + + .typing-indicator { + background-color: #555; + color: #fef6e4; + } + + .jump-to-bottom { + background-color: #fef6e4; + color: #001858; + border-color: #fef6e4; + } + + .jump-to-bottom:hover { + background-color: #8bd3dd; + } + + .chat-header { + border-color: #fef6e4; + } + + .chat-message-username { + color: #fef6e4; + } + + .chat-message-timestamp { + color: #aaa; + } + + .chat-message-content { + background-color: #555; + } + + .chat-input { + border-color: #fef6e4; + } + + .autocomplete-popup { + background-color: #444; + border-color: #fef6e4; + color: #fef6e4; + } + + .autocomplete-item:hover { + background-color: #555; + } + + .post-actions a { + color: #fef6e4; + border-color: #fef6e4; + } + + .post-actions a:hover { + background-color: #8bd3dd; + color: #001858; + } + + .reply-indicator { + background-color: #222; + color: #fef6e4; + } + + .reply-indicator button { + color: #fef6e4; + } + + .reply-indicator button:hover { + color: #f582ae; + } } /* Breadcrumb navigation */ @@ -809,4 +1242,4 @@ input.error, textarea.error, select.error { left: 10px; max-width: none; } -} \ No newline at end of file +} diff --git a/templates/base.html b/templates/base.html index a41938a..10b8efa 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,13 +5,14 @@ {{.Title}} + - + {{template "navbar" .}}
- {{block "content" .}}{{end}} + {{template .ContentTemplate .}}
{{template "cookie_banner" .}} -{{end}} \ No newline at end of file +{{end}} diff --git a/templates/pages/chat.html b/templates/pages/chat.html index 4d168db..d1f5e52 100644 --- a/templates/pages/chat.html +++ b/templates/pages/chat.html @@ -1,947 +1,60 @@ -{{define "chat"}} - - - - {{.Title}} - - - - - - {{template "navbar" .}} -
-
-
- Home - - Boards - - {{.Board.Name}} -
-
-
-
- Connecting... -
-

{{.Board.Name}}

-

{{.Board.Description}}

-
-
-
- ... -
- {{range .Messages}} -
-
- {{if .PfpFileID.Valid}} - PFP - {{else}} -
- {{end}} - {{.Username}} - {{.Timestamp.Format "02/01/2006 15:04"}} -
- {{if gt .ReplyTo 0}} -
Replying to message...
- {{end}} -
{{.Content}}
-
- Reply -
-
+{{define "chat"}}{{template "base" .}}{{end}} + +{{define "chat-content"}} +
+
+ Home + + Boards + + {{.Board.Name}} +
+
+
+
+ Connecting... +
+

{{.Board.Name}}

+

{{.Board.Description}}

+
+
+
+ ... +
+ {{range .Messages}} +
+
+ {{if .PfpFileID.Valid}} + PFP + {{else}} +
{{end}} + {{.Username}} + {{.Timestamp.Format "02/01/2006 15:04"}}
- -
-
- Replying to - -
- - + {{if gt .ReplyTo 0}} +
Replying to message...
+ {{end}} +
{{.Content}}
+
+ Reply
-
-
- {{template "cookie_banner" .}} - - - -{{end}} \ No newline at end of file + {{end}} + + +
+
+ Replying to + +
+ + +
+ +
+ +{{end}}