93 lines
4.3 KiB
Common Lisp
93 lines
4.3 KiB
Common Lisp
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
(in-package #:live-chat-ui)
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
(defvar *default-prefix* "/lisp-chat")
|
||
|
|
||
|
(defun set-subpath-prefix (prefix)
|
||
|
(setf *default-prefix* prefix))
|
||
|
|
||
|
(defun subpath-prefix (path)
|
||
|
(format nil "~a~a" *default-prefix* path))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; Render chat messages
|
||
|
(defun render-chat-messages ()
|
||
|
"Render the list of chat messages as HTML."
|
||
|
(let ((messages (fetch-messages)))
|
||
|
(if messages
|
||
|
(cl-who:with-html-output-to-string (*standard-output* nil :indent t)
|
||
|
(loop for msg in messages
|
||
|
do (cl-who:htm
|
||
|
(:div :class "box"
|
||
|
:style "overflow:auto;"
|
||
|
(cl-who:str msg)))))
|
||
|
"")))
|
||
|
|
||
|
;; Render chat UI
|
||
|
(defun render-chat-ui ()
|
||
|
"Render the main chat page with HTMX integration."
|
||
|
(cl-who:with-html-output-to-string (*standard-output* nil :prologue t :indent t)
|
||
|
(:html :lang "en"
|
||
|
(:head
|
||
|
(:meta :charset "utf-8")
|
||
|
(:meta :name "viewport" :content "width=device-width, initial-scale=1")
|
||
|
(:title "Live Chat")
|
||
|
(:link :rel "stylesheet"
|
||
|
:href "https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.2/css/bulma.min.css"
|
||
|
:integrity "sha512-RpeJZX3aH5oZN3U3JhE7Sd+HG8XQsqmP3clIbu4G28p668yNsRNj3zMASKe1ATjl/W80wuEtCx2dFA8xaebG5w=="
|
||
|
:crossorigin "anonymous"
|
||
|
:referrerpolicy "no-referrer")
|
||
|
(:script :src "https://cdnjs.cloudflare.com/ajax/libs/htmx/2.0.3/htmx.min.js"
|
||
|
:integrity "sha512-dQu3OKLMpRu85mW24LA1CUZG67BgLPR8Px3mcxmpdyijgl1UpCM1RtJoQP6h8UkufSnaHVRTUx98EQT9fcKohw=="
|
||
|
:crossorigin "anonymous"
|
||
|
:referrerpolicy "no-referrer")
|
||
|
(:script :src "https://cdnjs.cloudflare.com/ajax/libs/htmx/2.0.3/ext/ws.min.js"
|
||
|
:integrity "sha512-1OIiXEswZd/etj60BUwFmyoi0OhrWdoYlzayJpSBivoMV0pQPIQr+vtAn3W3htsbWtLRU8DrBl0epdK4DQbj/w=="
|
||
|
:crossorigin "anonymous"
|
||
|
:referrerpolicy "no-referrer"))
|
||
|
(:body
|
||
|
(:section :class "section"
|
||
|
(:div :class "container"
|
||
|
(:h1 :class "title" "Live Chat")
|
||
|
(:div :class "field has-addons"
|
||
|
(:div :class "control is-expanded"
|
||
|
(:input :class "input is-expanded"
|
||
|
:id "chat-input"
|
||
|
:autocomplete "off"
|
||
|
:placeholder "Enter your message..."
|
||
|
:type "text" :name "message"))
|
||
|
(:div :class "control"
|
||
|
(:button :class "button is-link is-light"
|
||
|
:type "submit" "Send")))))
|
||
|
(:section :class "section"
|
||
|
(:div :class "container"
|
||
|
:hx-ext "ws"
|
||
|
:ws-connect (subpath-prefix "/ws-chat-messages")
|
||
|
(:h3 :class "title is-3" "Chat Messages")
|
||
|
(:div :class "container"
|
||
|
:id "chat-messages"
|
||
|
:hx-get "/chat-messages"
|
||
|
(cl-who:str (render-chat-messages)))))
|
||
|
(:script "
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
function receivedMessage(msg) {
|
||
|
document.querySelector('#chat-messages')
|
||
|
.insertAdjacentHTML('afterbegin', msg.data);
|
||
|
}
|
||
|
|
||
|
// TODO {{ server-name }}:{{ server-port }}
|
||
|
const ws = new WebSocket('ws://' + window.location.host + '/ws-chat-messages');
|
||
|
ws.addEventListener('message', receivedMessage);
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const inputField = document.getElementById('chat-input');
|
||
|
inputField.addEventListener('keyup', (evt) => {
|
||
|
if (evt.key === 'Enter') {
|
||
|
ws.send(evt.target.value);
|
||
|
evt.target.value = '';
|
||
|
}
|
||
|
});")))))
|