summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README2
-rwxr-xr-xdb/0-create.psql13
-rwxr-xr-xsrc/site.clj35
-rwxr-xr-xstatic/chat.st92
-rwxr-xr-xstatic/index.html10
-rwxr-xr-xstatic/js/pichat.js99
-rwxr-xr-xtemplate/chat.st17
7 files changed, 108 insertions, 160 deletions
diff --git a/README b/README
index 0835e4f..12e0997 100644
--- a/README
+++ b/README
@@ -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>