Browse Source

User fullname and avatar are now in an iframe

Great performance boost.

Also:
* Input field to copy @username
  Once we can post, this may be handy
* Search is also an iframe (containing an iframe per user)
  So you can keep composing a twist while searching for usernames
* Should be responsive for cellulars (I can only resize browser
  to check. Got no phone. Anyway, the floating "Fork me" footer
  now has a link to navigation (local users), and that's about all
  the essential navigation. You can scroll a bit down from there
  for search results, or all the way up for the content.
* Make sure Dillo etc. that don't see bootstrap font icon
  can still have text links to everything

Enjoy
master
The Dod 11 years ago
parent
commit
3ffb1e6524
  1. 3
      static/assets/css/swizzler.css
  2. 77
      swizzler.py
  3. 3
      templates/footer.html
  4. 66
      templates/message.html
  5. 40
      templates/messages.html
  6. 37
      templates/sidebar.html
  7. 40
      templates/standard.html
  8. 16
      templates/twist.html
  9. 15
      twister.py

3
static/assets/css/swizzler.css

@ -1,5 +1,6 @@
body {padding-top:8px; background:#eef url(/assets/img/swizzler-128.png) no-repeat fixed right bottom} body {padding-top:8px; background:#eef url(/assets/img/swizzler-128.png) no-repeat fixed right bottom}
.avatar {max-width:64px; max-height:64px} .avatar {max-width:64px; max-height:64px}
.avatars32 .avatar {max-width:32px; max-height:32px} .avatars32 .avatar {max-width:32px; max-height:32px}
.avatars42 .avatar {max-width:44px; max-height:44px}
.panel, .list-group-item { background-color: rgba(255,255,255,0.33) } /* I should be doing this by customizing bootstrap. Later :) */ .panel, .list-group-item { background-color: rgba(255,255,255,0.33) } /* I should be doing this by customizing bootstrap. Later :) */
#footer {z-index:115;position:fixed;right:123px;bottom:0} #footer {z-index:115;position:fixed;right:96px;bottom:8px}

77
swizzler.py

@ -12,19 +12,19 @@ from ttp import ttp
class TwistParser(ttp.Parser): class TwistParser(ttp.Parser):
def format_tag(self, tag, text): def format_tag(self, tag, text):
'''Return formatted HTML for a hashtag.''' '''Return formatted HTML for a hashtag.'''
return '<a href="{0}/tag/{1}">{2}{3}</a>'.format( return '<a target="_top" href="{0}/tag/{1}">{2}{3}</a>'.format(
cherrypy.request.base+cherrypy.request.script_name, cherrypy.request.base+cherrypy.request.script_name,
ttp.urllib.quote(text.lower().encode('utf-8'),'xmlcharrefreplace'), tag, text) ttp.urllib.quote(text.lower().encode('utf-8'),'xmlcharrefreplace'), tag, text)
def format_username(self, at_char, user): def format_username(self, at_char, user):
'''Return formatted HTML for a username.''' '''Return formatted HTML for a username.'''
return '<a href="{0}/user/{1}">{2}{3}</a>'.format( return '<a target="_top" href="{0}/user/{1}">{2}{3}</a>'.format(
cherrypy.request.base+cherrypy.request.script_name, cherrypy.request.base+cherrypy.request.script_name,
user, at_char, user.lower()) user, at_char, user.lower())
def format_list(self, at_char, user, list_name): def format_list(self, at_char, user, list_name):
'''We don't have lists, so we see it as "@user" followed by "/something"''' '''We don't have lists, so we see it as "@user" followed by "/something"'''
return '<a href="{0}/user/{1}">{2}{3}</a>/{4}'.format( return '<a target="_top" href="{0}/user/{1}">{2}{3}</a>/{4}'.format(
cherrypy.request.base+cherrypy.request.script_name, cherrypy.request.base+cherrypy.request.script_name,
cherrypy.request.base+cherrypy.request.script_name, user, at_char, user.lower(), list_name) cherrypy.request.base+cherrypy.request.script_name, user, at_char, user.lower(), list_name)
@ -45,47 +45,47 @@ def format_trending(twister,num_messages=8):
### The Swizzler app ### The Swizzler app
class SwizzlerApp(object): class SwizzlerApp(object):
def _standard_params(self,twister,q,num_items=8): @cherrypy.expose
def search(self,q=''):
conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist)
result = {'site_root':cherrypy.request.base+cherrypy.request.script_name} result = {'site_root':cherrypy.request.base+cherrypy.request.script_name}
result['here'] = result['site_root']+cherrypy.request.path_info q = q.strip().split(' ')[0] # ignore anything after the first space if any
q=(q or '').strip().split(' ')[0] # ignore anything after the first space if any
if q.startswith('#'): # user said "#sometag", change to "sometag" if q.startswith('#'): # user said "#sometag", change to "sometag"
q=q[1:] q=q[1:]
if q and not q.startswith('@'): # Tag. redirect. if q and not q.startswith('@'): # Tag. redirect.
raise cherrypy.HTTPRedirect(result['site_root']+'/tag/{0}'.format(q)) raise cherrypy.HTTPRedirect(result['site_root']+'/tag/{0}'.format(q))
if q: if q:
result['user_prefix'] = q result['user_prefix'] = q
result['users'] = twister.get_users_by_partial_name(q[1:],num_items) result['users'] = twister.get_users_by_partial_name(q[1:],conf['num_messages'])
return result else:
result['trending'] = format_trending(twister,num_items) result['trending'] = format_trending(twister,conf['num_messages'])
return result return stache.render(stache.load_template('search'),result)
@cherrypy.expose @cherrypy.expose
def twist(self,username,k,q=None): def twist(self,username,k):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
twist = twister.get_twist(username,k) twist = twister.get_twist(username,k)
twist['style_large'] = True
rts = twister.get_twist_rts(username,k) rts = twister.get_twist_rts(username,k)
print rts
result = { result = {
'is_twist':True, 'is_twist':True,
'title':u"@{0}: {1} - Swizzler".format(twist['user']['username'],twist['time']), 'title':u"@{0}: {1} - Swizzler".format(username,twist['time']),
'twist':twist, 'twist':twist,
'in_reply_to':twist.get('reply') and twister.get_twist(twist['reply']['user']['username'],twist['reply']['k']) or None, 'in_reply_to':twist.get('reply') and twister.get_twist(twist['reply']['username'],twist['reply']['k']) or None,
'replies':twister.get_twist_replies(username,k), 'replies':twister.get_twist_replies(username,k),
'rts':rts, 'rts':rts,
'any_rts':not not rts, 'any_rts':not not rts,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('twist'),result) return stache.render(stache.load_template('twist'),result)
@cherrypy.expose @cherrypy.expose
def user(self,username,q=None): def user(self,username):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
user = twister.get_user_info(username) user = twister.get_user_info(username)
messages = twister.get_user_posts(username,conf['num_messages']) messages = twister.get_user_posts(username,conf['num_messages'])
result = { result = {
@ -96,14 +96,26 @@ class SwizzlerApp(object):
'any_messages':not not messages, 'any_messages':not not messages,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('standard'),result) return stache.render(stache.load_template('standard'),result)
@cherrypy.expose @cherrypy.expose
def tag(self,tag,q=None): def user_embed(self,username='nobody',style='normal'):
if username=='nobody': username='' # to enable /nobody/large
conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist)
user = twister.get_user_info(username)
result = {
'title':'@{0} - Swizzler'.format(username),
'site_root':cherrypy.request.base+cherrypy.request.script_name,
'user':twister.get_user_info(username)
}
result['style_{0}'.format(style)] = True
return stache.render(stache.load_template('user-iframe'),result)
@cherrypy.expose
def tag(self,tag):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
messages = twister.get_tag_posts(tag) messages = twister.get_tag_posts(tag)
result = { result = {
'is_tag':True, 'is_tag':True,
@ -113,15 +125,13 @@ class SwizzlerApp(object):
'any_messages':not not messages, 'any_messages':not not messages,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) 'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('standard'),result) return stache.render(stache.load_template('standard'),result)
@cherrypy.expose @cherrypy.expose
def home(self,localusername,mode='feed',q=None): def home(self,localusername,mode='feed'):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
menu = twister.local_user_menu(localusername) menu = twister.local_user_menu(localusername)
if mode=='mentions': if mode=='mentions':
messages = twister.get_user_mentions(localusername) messages = twister.get_user_mentions(localusername)
@ -131,20 +141,19 @@ class SwizzlerApp(object):
'is_home':True, 'is_home':True,
'is_mentions':mode=='mentions', 'is_mentions':mode=='mentions',
'is_feed':mode!='mentions', 'is_feed':mode!='mentions',
'title':u"{fullname} (@{username}): Home - Swizzler".format(**menu['active']), 'title':u"{fullname} (@{username}): {mode} - Swizzler".format(mode=mode=='mentions' and 'Mentions' or 'Home',**menu['active']),
'local_users':menu['users'], 'local_users':menu['users'],
'info':twister.get_info(), 'info':twister.get_info(),
'subject':menu['active'], 'subject':menu['active'],
'messages':messages, 'messages':messages,
'any_messages':not not messages, 'any_messages':not not messages,
'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('standard'),result) return stache.render(stache.load_template('standard'),result)
@cherrypy.expose @cherrypy.expose
def messages(self,localusername,remoteusername=None,q=None): def messages(self,localusername,remoteusername=None):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
localuser = twister.get_user_info(localusername) localuser = twister.get_user_info(localusername)
remoteuser = remoteusername and twister.get_user_info(remoteusername) or None remoteuser = remoteusername and twister.get_user_info(remoteusername) or None
threads = remoteusername and twister.get_user_messages(localusername,remoteusername,conf['num_messages']) or twister.get_user_messages(localusername) threads = remoteusername and twister.get_user_messages(localusername,remoteusername,conf['num_messages']) or twister.get_user_messages(localusername)
@ -158,18 +167,17 @@ class SwizzlerApp(object):
'any_threads':not not threads, 'any_threads':not not threads,
'local_users':twister.local_user_menu()['users'], 'local_users':twister.local_user_menu()['users'],
'info':twister.get_info(), 'info':twister.get_info(),
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) 'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('messages'),result) return stache.render(stache.load_template('messages'),result)
@cherrypy.expose @cherrypy.expose
def index(self,q=None): def index(self):
conf = cherrypy.request.app.config['swizzler'] conf = cherrypy.request.app.config['swizzler']
twister = Twister(conf['rpc_url'],format_twist) twister = Twister(conf['rpc_url'],format_twist)
params = self._standard_params(twister,q,conf['num_messages']) # called as soon as possible, because it might raise a redirect
messages = twister.get_sponsored_posts(conf['num_messages']) messages = twister.get_sponsored_posts(conf['num_messages'])
result = { result = {
'is_user':True, # i.e. we want to display "bio" and not mentions/DMs/profile buttons 'is_user':True, # i.e. we want to display "bio" and not mentions/DMs/profile buttons
'is_sponsored':True, # message template needs to know not to show "permalink"
'title':"Welcome to Swizzler", 'title':"Welcome to Swizzler",
'local_users':twister.local_user_menu('')['users'], # '' means: "Nobody" is active 'local_users':twister.local_user_menu('')['users'], # '' means: "Nobody" is active
'info':twister.get_info(), 'info':twister.get_info(),
@ -184,9 +192,8 @@ Start mining today, and all this (AND moral satisfaction) can be yours.""")
}, },
'messages':messages, 'messages':messages,
'any_messages':not not messages, 'any_messages':not not messages,
#the filter avoids some utf etc. that ttf can't handle (TODO: fix or replace format_twist) 'site_root':cherrypy.request.base+cherrypy.request.script_name,
} }
result.update(params)
return stache.render(stache.load_template('standard'),result) return stache.render(stache.load_template('standard'),result)
if __name__ == '__main__': if __name__ == '__main__':

3
templates/footer.html

@ -1,3 +1,4 @@
<footer id="footer"> <footer id="footer">
<div class="alert alert-info"><a target="_blank" href="https://github.com/swizzler/swizzler#readme"><strong>Fork</strong></a> this. Swizzler is <a target="_blank" href="http://www.gnu.org/licenses/agpl-3.0.html"><strong>AGPLV3</strong></a>.</span> <a class="btn btn-primary btn-sm" title="Scroll to navigation" href="#localusers"><span class="glyphicon glyphicon-th-list"></span></a>
<a class="badge" target="_blank" title="Fork it like you mean it" href="https://github.com/swizzler/swizzler#readme"><span class="glyphicon glyphicon-wrench"></a>
</footer> </footer>

66
templates/message.html

@ -1,45 +1,37 @@
<li class="list-group-item media"> <li class="list-group-item media">
{{#user}} <iframe class="thumbnail media-object {{#fromMe}}pull-right{{/fromMe}}{{^fromMe}}pull-left{{/fromMe}}"
style="border:none; padding:0; background:none; overflow:hidden"
width=33% height={{#style_large}}96{{/style_large}}{{^style_large}}72{{/style_large}} seamless=seamless
src="{{site_root}}/user_embed/{{username}}{{^username}}nobody{{/username}}{{#style_large}}/large{{/style_large}}{{#fromMe}}/from_me{{/fromMe}}"></iframe>
<div class="media-body">
{{#username}} {{#username}}
<a class="{{#fromMe}}pull-right{{/fromMe}}{{^fromMe}}pull-left{{/fromMe}} thumbnail" href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile"> <h5 class="media-heading">
<img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/genericPerson.png{{/avatar}}" alt="{{fullname}}"> {{^threads}}
</a> <span class="pull-right small">
{{^is_sponsored}}<a title="View twist details" href="{{site_root}}/twist/{{.}}/{{k}}">{{/is_sponsored}}{{time}}{{^is_sponsored}}</a>{{/is_sponsored}}
</span>
{{/threads}}
{{#rt_username}}
<small>
<a href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile">@{{.}}</a>
<span class="glyphicon glyphicon-retweet"></span>
</small>
{{/rt_username}}
<a href="{{site_root}}/user/{{username}}" title="@{{username}}'s profile"><strong>@{{username}}</strong></a>
{{^is_twist}}{{#reply}}
<small>
<span class="glyphicon glyphicon-share-alt"></span>
<a title="Re: @{{username}}'s twist" href="{{site_root}}/twist/{{username}}/{{k}}">@{{username}}</a>
</small>
{{/reply}}{{/is_twist}}
</h5>
{{/username}} {{/username}}
{{^username}} {{^username}}
<a class="pull-left thumbnail" href="#"> <h5 class="media-heading">
<img class="media-object avatar" src="{{site_root}}/assets/img/twister-64.jpg" alt="{{fullname}}"> <span class="pull-right small">{{time}}</span>
</a> <a href="{{site_root}}/">@{{username}}</a>
</h5>
{{/username}} {{/username}}
{{/user}}
<div class="media-body">
{{#user}}
{{#username}}
<h5 class="media-heading">
<span class="pull-right small">
<a title="View twist details" href="{{site_root}}/twist/{{.}}/{{k}}">{{time}}</a>
</span>
{{#rt_user}}
<small>
<a href="{{site_root}}/user/{{username}}" title="@{{username}}'s profile">{{fullname}}</a>
<span class="glyphicon glyphicon-retweet"></span>
</small>
{{/rt_user}}
<a href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile"><strong>{{fullname}}</strong></a>
{{^is_twist}}{{#reply}}
<small>
<span class="glyphicon glyphicon-share-alt"></span>
{{#user}}<a title="Re: @{{username}}'s twist" href="{{site_root}}/twist/{{username}}/{{k}}">{{fullname}}</a>{{/user}}
</small>
{{/reply}}{{/is_twist}}
</h5>
{{/username}}
{{^username}}
<h5 class="media-heading">
<span class="pull-right small">{{time}}</span>
<a href="{{site_root}}/">{{fullname}}</a>
</h5>
{{/username}}
{{/user}}
{{{message}}} {{{message}}}
</div> </div>
</li> </li>

40
templates/messages.html

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html language="{{lang}}"> <html language="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -22,22 +22,18 @@
<img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/twister-64.jpg{{/avatar}}" alt="{{fullname}}"> <img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/twister-64.jpg{{/avatar}}" alt="{{fullname}}">
</a> </a>
<div class="media-body"> <div class="media-body">
<h4 class="media-heading"> <h3 class="media-heading">
<a href="{{site_root}}/messages/{{username}}{{#remoteuser}}/{{username}}{{/remoteuser}}" <a class="label label-primary" href="{{site_root}}/messages/{{username}}{{#remoteuser}}/{{username}}{{/remoteuser}}"
title="@{{username}}'s messages{{#remoteuser}} with {{username}}{{/remoteuser}}" title="@{{username}}'s messages{{#remoteuser}} with {{username}}{{/remoteuser}}"
><span class="glyphicon glyphicon-envelope"></span></a> ><span class="glyphicon glyphicon-envelope"></span>
{{fullname}}'s direct messages {{fullname}}'s direct messages
{{#remoteuser}}with {{fullname}}{{/remoteuser}} {{#remoteuser}}with {{fullname}}{{/remoteuser}}</a>
</h3>
<h4>
<a class="label label-primary" href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span> Home</a>
<a class="label label-primary" href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span> Mentions</a>
<a class="label label-info" href="{{site_root}}/user/{{username}}" title="@{{username}}'s profile"><span class="glyphicon glyphicon-user"></span> Profile</a>
</h4> </h4>
{{#remoteuser}}
{{#subject}}
<a href="{{site_root}}/messages/{{username}}" title="Back to @{{username}}'s direct message summary"><span
class="glyphicon glyphicon-arrow-left"></span></a>
{{/subject}}
{{/remoteuser}}
<a href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a>
<a href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span></a>
<a href="{{site_root}}/user/{{username}}" title="@{{username}}'s profile"><span class="glyphicon glyphicon-user"></span></a>
</div> </div>
</div> </div>
{{/subject}} {{/subject}}
@ -49,21 +45,13 @@
<ul class="list-group media-list"> <ul class="list-group media-list">
{{#threads}} {{#threads}}
{{^remoteuser}} {{^remoteuser}}
{{#user}} <a class="label label-primary" href="{{site_root}}/messages/{{subject.username}}/{{username}}"
<div class="label label-info"> title="See more messages to/from @{{username}}">@{{username}} <span class="glyphicon glyphicon-arrow-right"></span></a>
<a href="{{site_root}}/messages/{{subject.username}}/{{username}}" title="See more messages to/from @{{username}}">{{fullname}}</a>
<a href="{{site_root}}/messages/{{subject.username}}/{{username}}" title="See more messages to/from @{{username}}"><span
class="glyphicon glyphicon-arrow-right"></span></a>
</div>
{{/user}}
{{/remoteuser}} {{/remoteuser}}
{{#remoteuser}} {{#remoteuser}}
{{#subject}} {{#subject}}
<div class="label label-info"> <a class="label label-primary" href="{{site_root}}/messages/{{subject.username}}"
<a href="{{site_root}}/messages/{{subject.username}}" title="Back to @{{username}}'s direct message summary"><span title="Back to @{{username}}'s direct message summary"><span class="glyphicon glyphicon-arrow-left"></span> Back</a>
class="glyphicon glyphicon-arrow-left"></span></a>
<a href="{{site_root}}/messages/{{subject.username}}" title="Back to @{{username}}'s direct message summary">Back</a>
</div>
{{/subject}} {{/subject}}
{{/remoteuser}} {{/remoteuser}}
<ul class="list-group media-list"> <ul class="list-group media-list">

37
templates/sidebar.html

@ -1,9 +1,9 @@
<div class="panel panel-primary"> <div id="localusers" class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
{{#info}} {{#info}}
<div class="pull-right badge">Peers: {{dht_nodes}}/{{dht_global_nodes}}</div> <div class="pull-right badge">Peers: {{dht_nodes}}/{{dht_global_nodes}}</div>
{{/info}} {{/info}}
<h3 class="panel-title"><span class="glyphicon glyphicon-home"></span> Local users</h3> <h3 class="panel-title"><span class="glyphicon glyphicon-th-list"></span> Local users</h3>
</div> </div>
<ul class="panel-body list-group media-list"> <ul class="panel-body list-group media-list">
{{#local_users}} {{#local_users}}
@ -18,17 +18,15 @@
href="{{site_root}}/{{#username}}home/{{.}}{{/username}}">{{/active}}{{fullname}}{{^active}}</a> href="{{site_root}}/{{#username}}home/{{.}}{{/username}}">{{/active}}{{fullname}}{{^active}}</a>
{{/active}} {{/active}}
</h5> </h5>
{{^active}}
{{#username}} {{#username}}
<a href="{{site_root}}/home/{{.}}" title="@{{.}}'s home"><span class="glyphicon glyphicon-home"></span></a> <a class="label label-primary" href="{{site_root}}/home/{{.}}" title="@{{.}}'s home"><span class="glyphicon glyphicon-home"></span></a>
<a href="{{site_root}}/home/{{.}}/mentions" title="mentions of @{{.}}"><span class="glyphicon glyphicon-bell"></span></a> <a class="label label-primary" href="{{site_root}}/home/{{.}}/mentions" title="mentions of @{{.}}"><span class="glyphicon glyphicon-bell"></span></a>
<a href="{{site_root}}/messages/{{.}}" title="direct messages from/to @{{.}}"><span class="glyphicon glyphicon-envelope"></span></a> <a class="label label-primary" href="{{site_root}}/messages/{{.}}" title="direct messages from/to @{{.}}"><span class="glyphicon glyphicon-envelope"></span></a>
<a href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile"><span class="glyphicon glyphicon-user"></span></a> <a class="label label-info" href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile"><span class="glyphicon glyphicon-user"></span></a>
{{/username}} {{/username}}
{{^username}} {{^username}}
(view sponsored posts) (view sponsored posts)
{{/username}} {{/username}}
{{/active}}
</div> </div>
</li> </li>
{{/local_users}} {{/local_users}}
@ -37,27 +35,16 @@
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"> <div class="panel-heading">
<form method=get action="{{here}}" class="inline-form" role="search"> <form target="searchresult" tag="searchresult" method=get action="{{site_root}}/search" class="inline-form" role="search">
<div class="form-group"> <div class="form-group">
<label for="#search"><span class="glyphicon glyphicon-search"></span> Search for tag or partial username</label> <label for="#search"><span class="glyphicon glyphicon-search"></span> Search for tag or partial username</label>
<input id="q" name="q" type="text" class="form-control" placeholder="#twister or @twi"{{#user_prefix}} value="{{.}}"{{/user_prefix}}> <input id="q" name="q" type="text" class="form-control" placeholder="#twister or @twi"{{#user_prefix}} value="{{.}}"{{/user_prefix}}>
</div> </div>
</form> </form>
</div>
<div class="panel-body">
{{#user_prefix}}
<div class="label label-primary"><span class="glyphicon glyphicon-tags"></span> User prefix search: <strong><em>{{user_prefix}}</em></strong></div>
<ul class="list-group">
{{#users}}<li class="list-group-item"><a href="{{site_root}}/user/{{.}}" title="@{{.}}'s profile">@{{.}}</a></li>{{/users}}
{{^users}}<li class="list-group-item"><h3><small>Nothing. Nada. Rien de rien. &#128557;</small></h3>{{/users}}
</ul>
{{/user_prefix}}
{{^user_prefix}}
<div class="label label-primary"><span class="glyphicon glyphicon-tags"></span> Trending tags</div>
<ul class="list-group">
{{#trending}}<li class="list-group-item">{{{.}}}</li>{{/trending}}
{{^trending}}<li class="list-group-item"><h3>Can't find trending tags. <small>Nothing. Nada. Rien de rien. &#128557;</small></h3>{{/trending}}
</ul>
{{/user_prefix}}
</div> </div>
</div> </div>
<iframe name="searchresult" class="thumbnail media-object {{#fromMe}}pull-right{{/fromMe}}{{^fromMe}}pull-left{{/fromMe}}"
style="width:100%; border:none; padding:0; background:none; overflow:hidden"
width=100 height=2645 seamless=seamless
src="{{site_root}}/search"></iframe>
</div>

40
templates/standard.html

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html language="{{lang}}"> <html language="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -24,34 +24,40 @@
<div class="media-body"> <div class="media-body">
{{#is_home}} {{#is_home}}
{{#username}} {{#username}}
<h4 class="media-heading"> <h3 class="media-heading">
{{#is_feed}} {{#is_feed}}
<a href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a> {{fullname}} <a class="label label-primary" href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span>
{{fullname}}'s home</a>
{{/is_feed}} {{/is_feed}}
{{#is_mentions}} {{#is_mentions}}
<a href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span></a> {{fullname}} <a class="label label-primary" href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span>
Mentions of {{fullname}}</a>
{{/is_mentions}} {{/is_mentions}}
{{#is_messages}} {{#is_messages}}
<a href="{{site_root}}/messages/{{username}}" title="direct messages to/from @{{username}}"><span class="glyphicon glyphicon-envelope"></span></a> {{fullname}} <a class="label label-primary" href="{{site_root}}/messages/{{username}}" title="direct messages to/from @{{username}}"><span class="glyphicon glyphicon-envelope"></span>
{{fullname}}</a>
{{/is_messages}} {{/is_messages}}
</h3>
<h4>
{{^is_feed}}
<a class="label label-primary" href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span>Home</a>
{{/is_feed}}
{{^is_mentions}}
<a class="label label-primary" href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span>Mentions</a>
{{/is_mentions}}
{{^is_messages}}
<a class="label label-primary" href="{{site_root}}/messages/{{username}}" title="direct messages from/to @{{username}}"><span class="glyphicon glyphicon-envelope"></span>Messages</a>
{{/is_messages}}
<a class="label label-info" href="{{site_root}}{{#username}}/user/{{.}}{{/username}}" title="@{{username}}'s profile"><span class="glyphicon glyphicon-user"></span>Profile</a>
</h4> </h4>
{{^is_feed}}
<a href="{{site_root}}/home/{{username}}" title="@{{username}}'s home"><span class="glyphicon glyphicon-home"></span></a>
{{/is_feed}}
{{^is_mentions}}
<a href="{{site_root}}/home/{{username}}/mentions" title="mentions of @{{username}}"><span class="glyphicon glyphicon-bell"></span></a>
{{/is_mentions}}
{{^is_messages}}
<a href="{{site_root}}/messages/{{username}}" title="direct messages from/to @{{username}}"><span class="glyphicon glyphicon-envelope"></span></a>
{{/is_messages}}
<a href="{{site_root}}/user/{{username}}" title="@{{username}}'s profile"><span class="glyphicon glyphicon-user"></span></a>
{{/username}} {{/username}}
{{/is_home}} {{/is_home}}
{{#is_user}} {{#is_user}}
<h4 class="media-heading"> <h4 class="media-heading">
{{#location}}<span class="pull-right small">{{.}}</span>{{/location}} {{#location}}<span class="pull-right small">{{.}}</span>{{/location}}
{{fullname}} <a class="label label-primary" href="{{site_root}}/user/{{username}}"><span class="glyphicon glyphicon-user"></span>
{{#url}}<small><a href="{{.}}">{{.}}</a></small>{{/url}}</h4> {{fullname}}</a>
{{#url}}<small><a target="_blank" href="{{.}}">{{.}}</a></small>{{/url}}</h4>
{{{bio}}} {{{bio}}}
{{/is_user}} {{/is_user}}
{{#is_tag}} {{#is_tag}}

16
templates/twist.html

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html language="{{lang}}"> <html language="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -28,14 +28,12 @@
{{#any_rts}} {{#any_rts}}
<div class="avatars32"> <div class="avatars32">
<h5 class="pull-left"><span class="glyphicon glyphicon-retweet"></span> Retwists:&nbsp;</h5>{{! I know it's ugly. I suck at CSS !}} <h5 class="pull-left"><span class="glyphicon glyphicon-retweet"></span> Retwists:&nbsp;</h5>{{! I know it's ugly. I suck at CSS !}}
{{#rts}}{{#rt_user}} {{#rts}}
<div class="thumbnail pull-left"> <iframe class="thumbnail pull-left"
<a href="{{site_root}}/user/{{username}}" title="Retwisted by {{fullname}} (@{{username}})"> style="border:none; padding:0; background:none; overflow:hidden"
<img class="media-object avatar" src="{{#avatar}}{{.}}{{/avatar}}{{^avatar}}/assets/img/genericPerson.png{{/avatar}}" width=84 height=72 seamless=seamless
alt="Retwisted by {{fullname}} (@{{username}})"> src="{{site_root}}/user_embed/{{rt_username}}/rt"></iframe>
</a> {{/rts}}
</div>
{{/rt_user}}{{/rts}}
</div> </div>
{{/any_rts}} {{/any_rts}}
{{/twist}} {{/twist}}

15
twister.py

@ -18,7 +18,7 @@ class Twister:
db.shelve.sync() db.shelve.sync()
def _format_reply(self,r): def _format_reply(self,r):
"gracefully fails if reply is empty" "gracefully fails if reply is empty"
return r and {"user":self.get_user_info(r['n']),'k':r['k']} or {} return r and {"user":self.get_user_info(r['n']),'username':r['n'],'k':r['k']} or {}
def _format_post_info(self,p): def _format_post_info(self,p):
result = { result = {
"height":p['userpost']['height'], "height":p['userpost']['height'],
@ -28,15 +28,18 @@ class Twister:
if p['userpost'].has_key('rt'): if p['userpost'].has_key('rt'):
result.update({ result.update({
"message":self._format_message(p['userpost']['rt']['msg']), "message":self._format_message(p['userpost']['rt']['msg']),
"user":self.get_user_info(p['userpost']['rt']['n']), "username":p['userpost']['rt']['n'],
#"user":self.get_user_info(p['userpost']['rt']['n']), ### too heavy. we do it in an iframe
"k":p['userpost']['rt']['k'], "k":p['userpost']['rt']['k'],
"rt_user":self.get_user_info(p['userpost']['n']), "rt_username":p['userpost']['n'],
#"rt_user":self.get_user_info(p['userpost']['n']), ### too heavy. we do it in an iframe
"reply":self._format_reply(p['userpost']['rt'].get('reply',{})), "reply":self._format_reply(p['userpost']['rt'].get('reply',{})),
}) })
else: else:
result.update({ result.update({
"message":self._format_message(p['userpost']['msg']), "message":self._format_message(p['userpost']['msg']),
"user":self.get_user_info(p['userpost']['n']), "username":p['userpost']['n'],
#"user":self.get_user_info(p['userpost']['n']), ### too heavy. we do it in an iframe
"k":p['userpost']['k'], "k":p['userpost']['k'],
"reply":self._format_reply(p['userpost'].get('reply',{})), "reply":self._format_reply(p['userpost'].get('reply',{})),
}) })
@ -47,6 +50,7 @@ class Twister:
if p: if p:
return self._format_post_info(p[0]['p']['v']) return self._format_post_info(p[0]['p']['v'])
raise SkipCache("Twist not found @{0}/{1}".format(username,k),{ raise SkipCache("Twist not found @{0}/{1}".format(username,k),{
"username":"",
"user":self.get_user_info('nobody'), "user":self.get_user_info('nobody'),
"k":0, # maybe something needs this "k":0, # maybe something needs this
"lastk":0, # or this "lastk":0, # or this
@ -125,10 +129,11 @@ class Twister:
if message['time'] > latest_ts: if message['time'] > latest_ts:
latest_ts = message['time'] latest_ts = message['time']
message['time'] = timestamp2iso(message['time']) message['time'] = timestamp2iso(message['time'])
message['username'] = message['fromMe'] and localusername or username
message['user'] = message['fromMe'] and localuser or user message['user'] = message['fromMe'] and localuser or user
message['message'] = self._format_message(message['text']) message['message'] = self._format_message(message['text'])
messages.insert(0,message) # reverse order (newer first) messages.insert(0,message) # reverse order (newer first)
result.append({'user':user,'messages':messages,'latest_ts':latest_ts}) result.append({'username':username,'user':user,'messages':messages,'latest_ts':latest_ts})
return sorted(result,key=lambda thread:thread['latest_ts'],reverse=True) return sorted(result,key=lambda thread:thread['latest_ts'],reverse=True)
@functioncache(60,ignore_instance=True) @functioncache(60,ignore_instance=True)

Loading…
Cancel
Save