diff options
| -rw-r--r-- | README | 2 | ||||
| -rwxr-xr-x | db/0-create.psql | 13 | ||||
| -rwxr-xr-x | src/site.clj | 35 | ||||
| -rwxr-xr-x | static/chat.st | 92 | ||||
| -rwxr-xr-x | static/index.html | 10 | ||||
| -rwxr-xr-x | static/js/pichat.js | 99 | ||||
| -rwxr-xr-x | template/chat.st | 17 |
7 files changed, 108 insertions, 160 deletions
@@ -1 +1 @@ -change +change me diff --git a/db/0-create.psql b/db/0-create.psql index ac0dac6..7c6b4b0 100755 --- a/db/0-create.psql +++ b/db/0-create.psql @@ -28,10 +28,23 @@ CREATE TABLE messages ( is_image bool NOT NULL ); +-- Queries to support: +-- 1) What are my favorite images? (By room, time, or author) +-- 2) Who favorited me? (By user, image, or time) +-- 3) What are the most favorited images? (By room, time, or author) +CREATE TABLE favorites ( + favorite_id SERIAL PRIMARY KEY, + src_user_id integer NOT NULL REFERENCES users, + message_id integer NOT NULL REFERENCES messages, + created_on timestamp NOT NULL DEFAULT now() +); + CREATE INDEX user_id_idx ON messages (user_id); CREATE INDEX room_id_idx ON messages (room_id); CREATE INDEX created_on_idx ON messages (created_on); +CREATE INDEX src_user_id_idx ON favorites (src_user_id); + INSERT INTO rooms (key, name, description, admin_only) VALUES ('RoomA', 'Room A', 'Hangout', false); INSERT INTO rooms (key, name, description, admin_only) diff --git a/src/site.clj b/src/site.clj index 5868f94..653e99a 100755 --- a/src/site.clj +++ b/src/site.clj @@ -2,7 +2,6 @@ (:import java.lang.System java.text.SimpleDateFormat java.util.Date - clojure.lang.PersistentQueue org.apache.commons.codec.digest.DigestUtils javax.servlet.http.Cookie org.antlr.stringtemplate.StringTemplateGroup) @@ -23,7 +22,7 @@ (.setRefreshInterval template-group 3) (defstruct user-struct :nick :user_id :avatar :last-seen) -(defstruct message-struct :nick :content :created_on) +(defstruct message-struct :nick :content :created_on :msg_id) (defn user-struct-from-session [session] (struct user-struct (session :nick) (session :user_id) (session :avatar) @@ -123,6 +122,7 @@ (defn process-message-for-output [d] {"nick" (encode-html-entities (d :nick)) + "message_id" (d :message_id) "created_on" (.format formatter (d :created_on)) "content" (encode-html-entities (d :content))}) @@ -164,7 +164,7 @@ (defn fetch-messages-by-room ([room-id image-only] (fetch-messages-by-room room-id image-only 0)) ([room-id image-only offset] - (let [query (str "SELECT m.content, m.created_on, u.nick " + (let [query (str "SELECT m.content, m.message_id, m.created_on, u.nick " "FROM messages m, users u " "WHERE room_id = ? AND m.user_id = u.user_id " (if image-only "AND m.is_image = true " "") @@ -358,13 +358,13 @@ (re-find pic-regex (strip-params content))) true false)) -(defn msg-db [user-id room-id msg] - (let [content (.trim (msg :content)) - is-image (is-image? content)] +(defn msg-db [user-id room-id content] + (let [is-image (is-image? content) + qry (str "INSERT INTO messages (user_id, room_id, content, is_image) " + "VALUES (?, ?, ?, ?) RETURNING message_id")] (with-connection db - (insert-values :messages - [:user_id :room_id :content :is_image] - [user-id room-id content is-image])))) + ((first (do-select [qry user-id room-id content is-image])) + :message_id)))) (defn msg [session params] (let [user-id (session :user_id) @@ -372,18 +372,17 @@ room-key (params :room) room (@rooms room-key) content (.trim (params :content)) - now (new Date) - msg (struct message-struct nick content now)] + now (new Date)] (cond (not room) (resp-error "BAD_ROOM") (not nick) (resp-error "NOT_LOGGED_IN") :else - (do - (dosync - (if (not (contains? @(room :users) nick)) - (login-user (user-struct-from-session session) room)) - (add-message msg room)) - (msg-db user-id (room :room_id) msg) - (resp-success "OK"))))) + (let [msg-id (msg-db user-id (room :room_id) content) + msg (struct message-struct nick content now msg-id)] + (dosync + (if (not (contains? @(room :users) nick)) + (login-user (user-struct-from-session session) room)) + (add-message msg room)) + (resp-success msg-id))))) (defn validated-msg [session params] (let [room-key (params :room) diff --git a/static/chat.st b/static/chat.st deleted file mode 100755 index aecf87c..0000000 --- a/static/chat.st +++ /dev/null @@ -1,92 +0,0 @@ -<html> - <head> - <title>dump.fm</title> - <link rel="stylesheet" type="text/css" href="/static/reset.css"> - <link rel="stylesheet" type="text/css" href="static/pichat.css"> - - <link rel="shortcut icon" href="/static/favicon.ico"> - <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> - <script type="text/javascript" src="/static/sha1.js"></script> - <script type="text/javascript" src="/static/pichat.js"></script> - <script type="text/javascript" src="/static/scroll.js"></script> - <script> - jQuery(document).ready(initChat); - var Nick = $json_user_nick$; - </script> - <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><style type="text/css"> -<!-- -a { - font-size: 12px; - color: #000; -} -a:link { - text-decoration: none; -} -a:visited { - text-decoration: none; - color: #000; -} -a:hover { - text-decoration: none; - color: #00F; -} -a:active { - text-decoration: none; - color: #000; -} - ---> -</style></head> - -<body> - - - $header()$ - -<div id="chatrap"><div id="logc"> - <div id="content"> - - <div id="messagetabs"></div> - <div id="rapper"> - <div id="loghead"></div> - </div> - <div id="userList"> - $users: { u | - <div class="username"><a href="/u/$u$" class="username"><img src="/static/cat.jpeg" width="50" height="50">$u$</a><br> - </div> - }$ - </div> - - <div id="messagePane"> - <div id="messageList"> - $messages: { m | - <div class="msgDiv oldmsg"><b><a href="/u/$m.nick$">$m.nick$</a>: </b> - <span class="content">$m.content$<span></div> - }$ - <hr /> - </div></div></div> - $if(user_nick)$ - <div id="msgInputDiv"> - <input id="msgInput" class="msgInput" type="input" /> - <input id="msgSubmit" type="submit" value="Send Image URL" - /> - </div> - $endif$ - </div> - </div> - - </div> - </div> - </div> - - </div> - - </div> - <div id="footerc"> - <p> - $footer()$</p> - <p> - </p> - </div> - </body> -</html> diff --git a/static/index.html b/static/index.html index cb1852b..f80e5da 100755 --- a/static/index.html +++ b/static/index.html @@ -1,9 +1,9 @@ </html><head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> - <script type="text/javascript" src="static/js/underscore-min.js"></script> - <script type="text/javascript" src="static/js/sha1.js"></script> - <script type="text/javascript" src="static/js/home.js"></script> - <script type="text/javascript" src="static/background.js"></script> + <script type="text/javascript" src="/static/js/underscore-min.js"></script> + <script type="text/javascript" src="/static/js/sha1.js"></script> + <script type="text/javascript" src="/static/js/home.js"></script> + <script type="text/javascript" src="static/background.js"></script> <script> $(document).ready(initHome); </script> @@ -224,4 +224,4 @@ body { </body> -</html>
\ No newline at end of file +</html> diff --git a/static/js/pichat.js b/static/js/pichat.js index b9e0d46..df79c17 100755 --- a/static/js/pichat.js +++ b/static/js/pichat.js @@ -24,20 +24,30 @@ function buildMsgContent(content) { return linkify(content) } -function buildMessageDiv(msg) { +var SpinnerImage = '<img class="spinner" src="/static/spinner.gif" />'; + +function buildMessageDiv(msg, isLoading) { var nick = escapeHtml(msg.nick); - return '<div class="msgDiv"><b><a href="/u/' + nick + ' ">' + nick + '</a>: </b>' - + buildMsgContent(msg.content) + '</div>'; + var msgId = !isLoading ? 'id="message-' + msg.message_id + '"' : ''; + var spinnerHtml = isLoading ? SpinnerImage : ''; + var loadingClass = isLoading ? ' loading' : ''; + return '<div class="msgDiv ' + loadingClass + '" ' + msgId + '>' + + '<b><a href="/u/' + nick + ' ">' + nick + '</a>: </b>' + + buildMsgContent(msg.content) + + spinnerHtml + + '</div>'; } function buildUserDiv(user) { if (user.avatar) { - return '<div class="username"><a href="/u/' + escapeHtml(user.nick) + '">' + - '<img src="' + user.avatar + '" width="50" height="50">' + - escapeHtml(user.nick) + '</a></div>'; + return '<div class="username">' + + '<a href="/u/' + escapeHtml(user.nick) + '" target="_blank">' + + '<img src="' + user.avatar + '" width="50" height="50">' + + escapeHtml(user.nick) + '</a></div>'; } else { - return '<div class="username"><a href="/u/' + escapeHtml(user.nick) + '">' + - escapeHtml(user.nick) + '</a></div>'; + return '<div class="username">' + + '<a href="/u/' + escapeHtml(user.nick) + '" target="_blank">' + + escapeHtml(user.nick) + '</a></div>'; } } @@ -49,10 +59,12 @@ function buildGrowlDataAndPopDatShit(msg) { } function growl(user, msg) { - $.gritter.add({ - title: user, - text: msg - }); + $.gritter.add({ + // (string | mandatory) the heading of the notification + title: user, + // (string | mandatory) the text inside the notification + text: msg + }); } function handleMsgError(resp) { @@ -72,11 +84,16 @@ function submitMessage() { PostedMessages.push(content); $('#msgInput').val(''); - updateUI([{ 'nick': Nick, 'content': content}], null); + var msg = { 'nick': Nick, 'content': content }; + var div = addNewMessage(msg, true); - var onSuccess = function(json) {}; - var onError = function(resp, textStatus, errorThrown) { - $('#msgInput, #msgSubmit').removeAttr('disabled'); + var onSuccess = function(json) { + div.attr('id', 'message-' + json) + .removeClass('loading').addClass('loaded'); + div.find('.spinner').remove(); + }; + var onError = function(resp, textStatus, errorThrown) { + div.remove(); handleMsgError(resp); }; @@ -89,7 +106,7 @@ function submitMessage() { dataType: 'json', success: onSuccess, error: onError - }); + }); } function ifEnter(fn) { @@ -112,30 +129,44 @@ function delayedScrollToBottom(delay) { setTimeout(scrollToBottom, delay, $('#messageList')[0]); } +function addNewMessages(msgs) { + var wasScrolledToBottom = isScrolledToBottom($('#messageList')[0]); + var msgStr = $.map(msgs, buildMessageDiv).join(''); + $('#messageList').append(msgStr); + + if (wasScrolledToBottom) { delayedScrollToBottom(500); } +} + +function addNewMessage(msg, isLoading) { + var wasScrolledToBottom = isScrolledToBottom($('#messageList')[0]); + var msgStr = buildMessageDiv(msg, isLoading); + var div = $(msgStr).appendTo('#messageList'); + if (wasScrolledToBottom) { delayedScrollToBottom(500); } + return div; +} + +function setUserList(users) { + $("#userList").html($.map(users, buildUserDiv).join('')); +} + function updateUI(msgs, users) { - if (window['growlize'] && msgs !== null) { - $.map(msgs, buildGrowlDataAndPopDatShit) - } - else if (msgs !== null) { - var msgStr = $.map(msgs, buildMessageDiv).join(''); - var wasScrolledToBottom = isScrolledToBottom($('#messageList')[0]); - $('#messageList').append(msgStr); - - if (wasScrolledToBottom) { - delayedScrollToBottom(500); - } + if (window['growlize'] && msgs && msgs.length > 0) { + $.map(msgs, buildGrowlDataAndPopDatShit) + } else if (msgs && msgs.length > 0) { + addNewMessages(msgs); } if (users !== null) { - var flattened = users.sort().join(",") - if (!('userlist' in cache) || flattened != cache.userlist){ - $("#userList").html($.map(users, buildUserDiv).join('')); - } - cache.userlist = flattened + var flattened = users.sort().join(",") + if (!('userlist' in cache) || flattened != cache.userlist) { + $("#userList").html($.map(users, buildUserDiv).join('')); + } + cache.userlist = flattened } } // A duplicate message is a message that was likely to have // originated from this browser. +// TODO: replace w/ msg_id checks. function isDuplicateMessage(m) { if (m.nick != Nick || $.inArray(m.content, PostedMessages) == -1) { return false; @@ -240,3 +271,5 @@ function initLog() { }); } + +function favoriteImage() {}; diff --git a/template/chat.st b/template/chat.st index 4b65aa7..7c723d7 100755 --- a/template/chat.st +++ b/template/chat.st @@ -49,7 +49,7 @@ <div id="messagePane"> <div id="messageList"> $messages: { m | - <div class="msgDiv oldmsg"><b><a href="/u/$m.nick$">$m.nick$</a>: </b> + <div class="msgDiv oldmsg" id="message-$m.message_id$"><b><a href="/u/$m.nick$">$m.nick$</a>: </b> <span class="content">$m.content$<span></div> }$ <hr /> @@ -64,17 +64,12 @@ </div> </div> </div> + </div> + <div id="footerc"> + <p> + $footer()$ + <p> </div> -</div> -</div> - - </div> - <div id="footerc"> - <p> - $footer()$</p> - <p> - </p> - </div> </body> </html> |
