Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Implement The Horn; #7 #145
Browse files Browse the repository at this point in the history
This is an implementation of communication between people on the site.
  • Loading branch information
chadwhitacre committed Jun 1, 2013
1 parent 89223bf commit dd9e271
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 2 deletions.
16 changes: 16 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,19 @@ BEGIN;
, mtime DESC;

END;


-------------------------------------------------------------------------------
-- https://github.com/gittip/www.gittip.com/issues/7
-- https://github.com/gittip/www.gittip.com/issues/145

CREATE TABLE toots
( id bigserial PRIMARY KEY
, ctime timestamp with time zone NOT NULL
DEFAULT CURRENT_TIMESTAMP
, tooter text NOT NULL REFERENCES participants
ON UPDATE CASCADE ON DELETE RESTRICT
, tootee text NOT NULL REFERENCES participants
ON UPDATE CASCADE ON DELETE RESTRICT
, toot text NOT NULL
);
65 changes: 65 additions & 0 deletions templates/gittip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,71 @@ UL.community-memberships {
}


#horn {
textarea {
width: 100%;
height: 40px;
font: normal 16px/20px $Helvetica;
}
.help {
font: normal 10px/12px $Helvetica;
color: $brown;
text-transform: uppercase;
display: block;

span {
display: inline-block;
white-space: nowrap;
padding: 2pt 0;
}
}
.datetime {
display: block;
padding: 0 0 3pt;
}
button#toot-button {
float: right;
}
li {
margin: 0;
padding: 0 0 2pt;

&.me.theirs {
font: bold 18px/19px $Helvetica;
}
&.them {
font: normal 12px/13px $Helvetica;
}
&.them.own {
font-weight: bold;
}
.meta {
text-align: right;
text-transform: none;
font: normal 11px/13px $Helvetica;
color: $light-brown;

span {
display: inline-block;
white-space: nowrap;
padding: 2pt 0;
}

.re {
text-align: left;
padding-right: 3pt;
}
.tootee {
font-weight: bold;
}
.time {
font-style: italic;
}
}
}
}


#history {
th {
padding: 0 3pt;
Expand Down
9 changes: 8 additions & 1 deletion templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
{% if participant == user or user.ADMIN %}
<div class="nav level-2">
<h2>Navigation</h2>
{% set pages = [('/', 'Profile'), ('/giving/', 'Giving'), ('/history/', 'History'), ('/widgets/', 'Widgets')] %}
{% set pages = [('/', 'Profile'), ('/horn/', 'Horn'), ('/giving/', 'Giving'), ('/history/', 'History'), ('/widgets/', 'Widgets')] %}
{% for slug, name in pages %}
<a href="/{{ participant.username }}{{ slug }}"><button{% if slug.strip('/') == current_page %} class="selected"{% end %}>{{ name }}</button></a>
{% end %}
Expand All @@ -29,7 +29,14 @@ <h2>Navigation</h2>
<div class="nav level-2">
<h2>Navigation</h2>
<a href="/{{ participant.username }}/"><button{% if '' == current_page %} class="selected"{% end %}>Profile</button></a>
<a href="/{{ participant.username }}/horn/"><button{% if 'horn' == current_page %} class="selected"{% end %}>Horn</button></a>
<a href="/{{ participant.username }}/split.html"><button{% if 'split.html' == current_page %} class="selected"{% end %}>Split</button></a>
</div>
{% else %}
<div class="nav level-2">
<h2>Navigation</h2>
<a href="/{{ participant.username }}/"><button{% if '' == current_page %} class="selected"{% end %}>Profile</button></a>
<a href="/{{ participant.username }}/horn/"><button{% if 'horn' == current_page %} class="selected"{% end %}>Horn</button></a>
</div>
{% end %}
{% end %}
41 changes: 41 additions & 0 deletions www/%username/horn/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from aspen import Response
from gittip.utils import get_participant
from gittip import AMOUNTS
# ========================================================================== ^L

participant = get_participant(request, restrict=False)
hero = "Horn"
title = "%s - %s" % (participant.username, hero)
locked = False

# ========================================================================== ^L
{% extends templates/profile.html %}
{% block page %}
<script>$(document).ready(Gittip.horn.init);</script>
<div class="col0" id="horn">
{% if not user.ANON %}
{% if user == participant %}
<h2>Toot your own horn!</h2>
{% else %}
<h2>Toot their horn!</h2>
{% end %}
<form id="toot-form">
<textarea id="toot"></textarea>
<p class="help">
<span>
{% if user == participant %}
What have you done today that's awesome? Don't be shy!<br />
Anyone who gave you money last week will see this on their Horn.
{% else %}
Why do you appreciate {{ participant.username }}?
{% end %}
</span>
<button id="toot-button" type="submit">Toot</button>
</p>
<div class="clear"></div>
</form>
{% end %}
<h2>Toots</h2>
<ul id="toots"></ul>
</div>
{% end %}
16 changes: 16 additions & 0 deletions www/%username/horn/toot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from aspen import Response
from gittip import db

# ============================= ^L

if user.ANON:
raise Response(400)
tooter = user.username
tootee = path['username']
toot= body['toot']
if len(toot) > 140:
raise Response(400)
db.execute( "INSERT INTO toots (tooter, tootee, toot) VALUES (%s, %s, %s)"
, (tooter, tootee, toot)
)
response.code = 204
61 changes: 61 additions & 0 deletions www/%username/horn/toots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Return an array of toots for this participant.

If the user is the participant, then the toot stream will include toots
referring to people they gave money to last week.

"""
from gittip import db
from gittip.utils import get_participant

SQL_SELF = """

SELECT id, ctime, %s as horn, tootee, tooter = tootee AS own, toot
FROM toots
WHERE ( tootee = %s
OR tootee IN ( SELECT tippee
FROM transfers
WHERE tipper=%s
AND "timestamp" < (now() - interval '8 days')
)
)
AND id > %s
AND id <= %s
ORDER BY id DESC
LIMIT %s

"""

SQL_OTHER = """

SELECT id, ctime, %s as horn, tootee, tooter = tootee AS own, toot
FROM toots
WHERE tootee = %s
AND id > %s
AND id <= %s
ORDER BY id DESC
LIMIT %s

"""

DEFAULT_LIMIT = 200
# =========================== ^L
participant = get_participant(request, restrict=False)
username = participant.username
try:
limit = min(int(qs.get('limit', DEFAULT_LIMIT)), DEFAULT_LIMIT)
since_id = long(qs.get('since_id', 0))
if user == participant:
SQL = SQL_SELF
args = [username, username, username, since_id, limit]
else:
SQL = SQL_OTHER
args = [username, username, since_id, limit]

max_id = qs.get('max_id')
if max_id is None:
SQL = SQL.replace("AND id <= %s", "")
else:
args.insert(-1, long(max_id))
except ValueError: # cast to int/long failed
raise Response(400)
response.body = list(db.fetchall(SQL, tuple(args)))
2 changes: 1 addition & 1 deletion www/assets/%version/gittip.css

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions www/assets/%version/gittip.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ if(!Array.prototype.remove)
}
};

function prettyDate(time) {
// http://ejohn.org/blog/javascript-pretty-date/
var munged = (time || "").replace(/-\d\d:\d\d$/,"")
.replace(/-/g,"/")
.replace(/[TZ]/g," "),
date = new Date(munged),
diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
console.log(time, munged, date, diff, day_diff);
if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
return;

return day_diff == 0 && (
diff < 60 && "just now" ||
diff < 120 && "1 minute" ||
diff < 3600 && Math.floor( diff / 60 ) + " minutes" ||
diff < 7200 && "1 hour" ||
diff < 86400 && Math.floor( diff / 3600 ) + " hours") ||
day_diff == 1 && "Yesterday" ||
day_diff < 7 && day_diff + " days" ||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks";
}

// Main namespace.
// ===============
Expand Down Expand Up @@ -729,3 +751,78 @@ Gittip.communities.leave = function(name, callback)
Gittip.communities.update(name, false, callback);

}


// Horns
// =====

Gittip.horn = {};

Gittip.horn.init = function()
{
Gittip.horn.since_id = undefined;
$('#toot-form').submit(Gittip.horn.toot);
Gittip.horn.update({limit: 20});
};

Gittip.horn.update = function(data)
{
clearTimeout(Gittip.horn.handle);
data = data || {};
if (Gittip.horn.since_id !== undefined)
data.since_id = Gittip.horn.since_id;
jQuery.ajax(
{ type: "GET"
, url: "toots.json"
, data: data
, success: Gittip.horn.draw
});
};

Gittip.horn.draw = function(toots)
{
for (var i=toots.length-1, toot; toot = toots[i]; i--)
{
Gittip.horn.since_id = toot.id;
Gittip.horn.drawOne(toot);
}
Gittip.horn.handle = setTimeout(Gittip.horn.update, 1000)
};

Gittip.horn.drawOne = function(toot)
{
var escaped = $('<div>').text(toot.toot).html();
var html = '<li class="'
+ (toot.horn === toot.tootee ? 'me' : 'them')
+ ' '
+ (toot.own ? 'own' : 'theirs')
+ '"><span class="toot">'
+ ( toot.tootee !== toot.horn && !toot.own
? 're: ' + toot.tootee + ': '
: ''
)
+ escaped
+ ( toot.tootee !== toot.horn && toot.own
? '&mdash;' + toot.tootee
: ''
)
+ '</div>'
+ '</span>'
+ '</li>'
$('#toots').prepend(html)
};

Gittip.horn.toot = function(e)
{
e.preventDefault();
e.stopPropagation();
var toot = $('#toot').val();

jQuery.ajax(
{ type: "POST"
, url: "toot.json"
, data: {toot: toot}
, success: Gittip.horn.update
});
return false;
};

0 comments on commit dd9e271

Please sign in to comment.