mirror of https://git.skyjake.fi/gemini/bubble.git
Flairs: Internal markup, improved rendering with detail levels
This commit is contained in:
parent
8737fc20de
commit
fb83c111e3
12
50_bubble.py
12
50_bubble.py
|
@ -249,6 +249,13 @@ Bubble is open source:
|
|||
|
||||
return f'=> /dashboard {self.user.avatar} {self.user.name}{notifs}{mode}\n'
|
||||
|
||||
def feed_flair(self, post, context):
|
||||
flair = User.render_flair(post.poster_flair,
|
||||
context,
|
||||
abbreviate=True,
|
||||
user_mod=post.user in self.context_mod_ids)
|
||||
return f' [{flair}]' if flair else ''
|
||||
|
||||
def feed_entry(self, post, context=None, omit_rotate_info=False, is_activity_feed=False):
|
||||
is_issue_tracker = self.is_context_tracker
|
||||
is_comment = post.parent != 0 # Flat feeds intermingle comments with posts.
|
||||
|
@ -276,7 +283,6 @@ Bubble is open source:
|
|||
else:
|
||||
age = post.age(tz=self.tz)
|
||||
bell = ' 🔔' if post.num_notifs else ''
|
||||
flair = f' [{post.poster_flair}]' if post.poster_flair else ''
|
||||
|
||||
SHORT_PREVIEW_LEN = 160
|
||||
|
||||
|
@ -320,7 +326,7 @@ Bubble is open source:
|
|||
post_icon = post.poster_avatar
|
||||
post_label = post.poster_name
|
||||
if not (is_user_post and context and context.id == post.subspace):
|
||||
post_label += flair
|
||||
post_label += self.feed_flair(post, context)
|
||||
post_path = f'/u/{post.poster_name}'
|
||||
meta_icon = '💬'
|
||||
|
||||
|
@ -352,7 +358,7 @@ Bubble is open source:
|
|||
# Last line in the metadata.
|
||||
meta = []
|
||||
if is_comment or (sub and not is_activity_feed):
|
||||
meta.append(post.poster_name + flair)
|
||||
meta.append(post.poster_name + self.feed_flair(post, context))
|
||||
if cmt:
|
||||
meta.append(cmt)
|
||||
if likes:
|
||||
|
|
|
@ -32,4 +32,7 @@ UPDATE users SET notif=notif|0x040000;
|
|||
UPDATE users SET notif=notif|0x100000;
|
||||
|
||||
-- Migration from v7 to v8 --
|
||||
ALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';
|
||||
ALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';
|
||||
|
||||
-- Migration from v8.0 to v8.1 --
|
||||
ALTER TABLE users MODIFY COLUMN flair VARCHAR(1000) DEFAULT '';
|
||||
|
|
40
feeds.py
40
feeds.py
|
@ -298,7 +298,10 @@ def make_post_page_or_configure_feed(session):
|
|||
if session.c_user:
|
||||
page = f'# {session.c_user.avatar} {session.c_user.name}\n'
|
||||
if session.c_user.flair:
|
||||
page += f"[{session.c_user.flair}]\n"
|
||||
flair = User.render_flair(session.c_user.flair, session.context,
|
||||
long_form=True, db=session.db)
|
||||
if flair:
|
||||
page += f"\n{flair}\n"
|
||||
else:
|
||||
page = f'# {subspace.title()}\n'
|
||||
page += f'=> /{subspace.title()} {subspace.title()}\n'
|
||||
|
@ -345,13 +348,13 @@ def make_post_page(session, post):
|
|||
page = ''
|
||||
focused_cmt = None
|
||||
|
||||
def commenter_flair(cmt, post):
|
||||
cmt_flair = [cmt.poster_flair] if cmt.poster_flair else []
|
||||
if post and cmt.user == post.user:
|
||||
cmt_flair = ['op'] + cmt_flair
|
||||
elif cmt.user in session.context_mod_ids:
|
||||
cmt_flair = ['mod'] + cmt_flair
|
||||
return f" [{', '.join(cmt_flair)}]" if cmt_flair else ""
|
||||
def commenter_flair(cmt, post, abbreviate, with_context=None):
|
||||
flair = User.render_flair(cmt.poster_flair,
|
||||
with_context if with_context else session.context,
|
||||
abbreviate=abbreviate,
|
||||
user_mod=cmt.user in session.context_mod_ids,
|
||||
user_op=post and cmt.user == post.user)
|
||||
return f' [{flair}]' if flair else ''
|
||||
|
||||
if is_comment_page:
|
||||
# Switch to the parent post, but display it in preview mode.
|
||||
|
@ -370,7 +373,9 @@ def make_post_page(session, post):
|
|||
return 51, 'Not found'
|
||||
page += f'=> /help/deleted-post 🔒 Comment on a deleted post (ID:{post_id})\n\n'
|
||||
page += session.render_post(focused_cmt)
|
||||
flair = commenter_flair(focused_cmt, post)
|
||||
flair = commenter_flair(focused_cmt, post,
|
||||
abbreviate=False,
|
||||
with_context=db.get_subspace(post.subspace))
|
||||
page += f'\n=> /u/{focused_cmt.poster_name} {focused_cmt.poster_avatar} {focused_cmt.poster_name}{flair}\n'
|
||||
page += f'{focused_cmt.age()}\n'
|
||||
|
||||
|
@ -449,7 +454,13 @@ def make_post_page(session, post):
|
|||
page += '\n'
|
||||
if post.tags:
|
||||
page += '### ' + post.tags + '\n'
|
||||
flair = f" [{post.poster_flair}]" if post.poster_flair else ""
|
||||
#flair = f" [{post.poster_flair}]" if post.poster_flair else ""
|
||||
|
||||
flair = User.render_flair(post.poster_flair,
|
||||
session.context,
|
||||
user_mod=post.user in session.context_mod_ids)
|
||||
if flair: flair = f" [{flair}]"
|
||||
|
||||
poster_link = f'=> /u/{post.poster_name} {post.poster_avatar} {post.poster_name}{flair}\n'
|
||||
if session.is_context_tracker:
|
||||
page += f'=> /{session.context.title()} 🐞 Issue #{post.issueid} in {session.context.title()}\n'
|
||||
|
@ -590,7 +601,7 @@ def make_post_page(session, post):
|
|||
cmt.ymd_hm(tz=session.tz, date_fmt='%b %d', time_prefix='at ') if elapsed_hours < 24 * 180 else \
|
||||
cmt.ymd_hm(tz=session.tz, time_prefix='at ')
|
||||
|
||||
cmt_flair = commenter_flair(cmt, post)
|
||||
cmt_flair = commenter_flair(cmt, post, abbreviate=True)
|
||||
if not session.is_archive:
|
||||
src = f'=> /u/{cmt.poster_name}/{cmt.id} {cmt.poster_avatar} {cmt.poster_name}{cmt_flair} · {comment_age}:\n'
|
||||
else:
|
||||
|
@ -715,13 +726,14 @@ def make_feed_page(session):
|
|||
elif not context:
|
||||
topinfo += f"{session.bubble.site_info if session.user else session.bubble.site_info_nouser}\n"
|
||||
else:
|
||||
if c_user and (c_user.info or c_user.url):
|
||||
if c_user and (c_user.info or c_user.url or c_user.flair):
|
||||
if c_user.info:
|
||||
topinfo += c_user.info + '\n'
|
||||
if c_user.flair:
|
||||
topinfo += f"[{c_user.flair}]\n"
|
||||
if c_user.url:
|
||||
topinfo += f'=> {c_user.url}\n'
|
||||
if c_user.flair:
|
||||
flair = User.render_flair(c_user.flair, context=None, long_form=True, db=session.db)
|
||||
topinfo += f'\n{flair}'
|
||||
elif context:
|
||||
if context.info:
|
||||
topinfo += context.info + '\n'
|
||||
|
|
95
model.py
95
model.py
|
@ -233,6 +233,15 @@ class User:
|
|||
# Roles:
|
||||
BASIC, ADMIN, LIMITED = range(3)
|
||||
|
||||
# Flair types:
|
||||
FLAIRS = {
|
||||
'♡': 'Self description',
|
||||
'🏝️': 'Absence',
|
||||
'✍️': 'Writing style',
|
||||
'🗣️': 'Interaction style',
|
||||
'🛂': 'Note from moderator',
|
||||
}
|
||||
|
||||
# Sort modes:
|
||||
SORT_POST_RECENT = 'r'
|
||||
SORT_POST_HOTNESS = 'h'
|
||||
|
@ -289,6 +298,90 @@ class User:
|
|||
|
||||
return None
|
||||
|
||||
def render_flair(user_flair, context, abbreviate=False, long_form=False, db=None,
|
||||
user_mod=False, user_op=False):
|
||||
"""
|
||||
Arguments:
|
||||
db (Database): database object for looking up subspace names
|
||||
in the long form.
|
||||
context (Subspace): where the flair is being shown. If None,
|
||||
the flair is being shown in the home feed.
|
||||
abbreviate (bool): user-provided text is omitted and only
|
||||
icons are shown.
|
||||
long_form (bool): a description of the flair type is included
|
||||
and the output is formatted onto multiple lines instead
|
||||
of being a single line.
|
||||
"""
|
||||
if not user_flair.strip():
|
||||
return ''
|
||||
|
||||
out = ''
|
||||
|
||||
for flair in user_flair.split('\n'):
|
||||
pos = flair.find(':')
|
||||
scope = flair[:pos].strip() if pos >= 0 else None
|
||||
is_admin_assigned = (scope and scope.startswith('*'))
|
||||
if is_admin_assigned:
|
||||
scope = scope[1:]
|
||||
label = flair[pos + 1:].strip() if pos >= 0 else flair
|
||||
icon = ''
|
||||
if len(label):
|
||||
for key in User.FLAIRS:
|
||||
if label.startswith(key):
|
||||
icon = key
|
||||
label = label[len(key):].strip()
|
||||
break
|
||||
|
||||
#print(user_flair, icon, label, scope)
|
||||
|
||||
has_abbrev = False
|
||||
|
||||
if long_form:
|
||||
# Show everything in the long form.
|
||||
if icon:
|
||||
out += icon + ' '
|
||||
if scope:
|
||||
subspace = db.get_subspace(id=abs(int(scope)))
|
||||
if subspace:
|
||||
scope = f" (in {subspace.title()})"
|
||||
else:
|
||||
scope = " (in a deleted subspace)"
|
||||
else:
|
||||
scope = ''
|
||||
if icon:
|
||||
out += f"{User.FLAIRS[icon]}: "
|
||||
elif not scope and is_admin_assigned:
|
||||
out += '📛 Assigned flair: '
|
||||
else:
|
||||
out += '📛 Personal flair: '
|
||||
out += f"{label}{scope}{' (set by admin)' if is_admin_assigned else ''}\n"
|
||||
|
||||
elif not scope or (context and int(scope) == context.id):
|
||||
# A global flair is displayed everywhere, otherwise the scope must match
|
||||
# current context.
|
||||
if len(out): out += ' ' if abbreviate else ', '
|
||||
out += icon
|
||||
if not abbreviate or not scope:
|
||||
if len(out): out += ' '
|
||||
out += label
|
||||
elif not icon:
|
||||
has_abbrev = True
|
||||
|
||||
if not long_form:
|
||||
if user_op or user_mod:
|
||||
if len(out):
|
||||
out = ', ' + out
|
||||
if user_op and user_mod:
|
||||
out = 'OP/mod' + out
|
||||
elif user_op:
|
||||
out = 'OP' + out
|
||||
elif user_mod:
|
||||
out = 'mod' + out
|
||||
if has_abbrev:
|
||||
out += '...'
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Subspace:
|
||||
OMIT_FROM_ALL_FLAG = 0x1
|
||||
|
@ -500,7 +593,7 @@ class Database:
|
|||
db.execute("""CREATE TABLE IF NOT EXISTS users (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(30) UNIQUE,
|
||||
flair VARCHAR(30) DEFAULT '',
|
||||
flair VARCHAR(1000) DEFAULT '',
|
||||
info VARCHAR(1000) DEFAULT '',
|
||||
url VARCHAR(1000) DEFAULT '',
|
||||
recovery VARCHAR(1000) DEFAULT '',
|
||||
|
|
Loading…
Reference in New Issue