=> fb83c111e3aadd361ae323dbcb3c40aea54d803a
[1mdiff --git a/50_bubble.py b/50_bubble.py[m [1mindex a1fc72f..a0a236c 100644[m [1m--- a/50_bubble.py[m [1m+++ b/50_bubble.py[m [36m@@ -249,6 +249,13 @@[m [mBubble is open source:[m [m return f'=> /dashboard {self.user.avatar} {self.user.name}{notifs}{mode}\n'[m [m [32m+[m[32m def feed_flair(self, post, context):[m [32m+[m[32m flair = User.render_flair(post.poster_flair,[m [32m+[m[32m context,[m [32m+[m[32m abbreviate=True,[m [32m+[m[32m user_mod=post.user in self.context_mod_ids)[m [32m+[m[32m return f' [{flair}]' if flair else ''[m [32m+[m def feed_entry(self, post, context=None, omit_rotate_info=False, is_activity_feed=False):[m is_issue_tracker = self.is_context_tracker[m is_comment = post.parent != 0 # Flat feeds intermingle comments with posts.[m [36m@@ -276,7 +283,6 @@[m [mBubble is open source:[m else:[m age = post.age(tz=self.tz)[m bell = ' ๐' if post.num_notifs else ''[m [31m- flair = f' [{post.poster_flair}]' if post.poster_flair else ''[m [m SHORT_PREVIEW_LEN = 160[m [m [36m@@ -320,7 +326,7 @@[m [mBubble is open source:[m post_icon = post.poster_avatar[m post_label = post.poster_name[m if not (is_user_post and context and context.id == post.subspace):[m [31m- post_label += flair[m [32m+[m[32m post_label += self.feed_flair(post, context)[m post_path = f'/u/{post.poster_name}'[m meta_icon = '๐ฌ'[m [m [36m@@ -352,7 +358,7 @@[m [mBubble is open source:[m # Last line in the metadata.[m meta = [][m if is_comment or (sub and not is_activity_feed):[m [31m- meta.append(post.poster_name + flair)[m [32m+[m[32m meta.append(post.poster_name + self.feed_flair(post, context))[m if cmt:[m meta.append(cmt)[m if likes:[m [1mdiff --git a/db-migrate.sql b/db-migrate.sql[m [1mindex 5728e8a..cc2fc41 100644[m [1m--- a/db-migrate.sql[m [1m+++ b/db-migrate.sql[m [36m@@ -32,4 +32,7 @@[m [mUPDATE users SET notif=notif|0x040000;[m UPDATE users SET notif=notif|0x100000;[m [m -- Migration from v7 to v8 --[m [31m-ALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';[m \ No newline at end of file[m [32m+[m[32mALTER TABLE users ADD COLUMN flair VARCHAR(30) DEFAULT '';[m [32m+[m [32m+[m[32m-- Migration from v8.0 to v8.1 --[m [32m+[m[32mALTER TABLE users MODIFY COLUMN flair VARCHAR(1000) DEFAULT '';[m [1mdiff --git a/feeds.py b/feeds.py[m [1mindex 66e86a4..5111fa7 100644[m [1m--- a/feeds.py[m [1m+++ b/feeds.py[m [36m@@ -298,7 +298,10 @@[m [mdef make_post_page_or_configure_feed(session):[m if session.c_user:[m page = f'# {session.c_user.avatar} {session.c_user.name}\n'[m if session.c_user.flair:[m [31m- page += f"[{session.c_user.flair}]\n"[m [32m+[m[32m flair = User.render_flair(session.c_user.flair, session.context,[m [32m+[m[32m long_form=True, db=session.db)[m [32m+[m[32m if flair:[m [32m+[m[32m page += f"\n{flair}\n"[m else:[m page = f'# {subspace.title()}\n'[m page += f'=> /{subspace.title()} {subspace.title()}\n'[m [36m@@ -345,13 +348,13 @@[m [mdef make_post_page(session, post):[m page = ''[m focused_cmt = None[m [m [31m- def commenter_flair(cmt, post):[m [31m- cmt_flair = [cmt.poster_flair] if cmt.poster_flair else [][m [31m- if post and cmt.user == post.user:[m [31m- cmt_flair = ['op'] + cmt_flair[m [31m- elif cmt.user in session.context_mod_ids:[m [31m- cmt_flair = ['mod'] + cmt_flair[m [31m- return f" [{', '.join(cmt_flair)}]" if cmt_flair else ""[m [32m+[m[32m def commenter_flair(cmt, post, abbreviate, with_context=None):[m [32m+[m[32m flair = User.render_flair(cmt.poster_flair,[m [32m+[m[32m with_context if with_context else session.context,[m [32m+[m[32m abbreviate=abbreviate,[m [32m+[m[32m user_mod=cmt.user in session.context_mod_ids,[m [32m+[m[32m user_op=post and cmt.user == post.user)[m [32m+[m[32m return f' [{flair}]' if flair else ''[m [m if is_comment_page:[m # Switch to the parent post, but display it in preview mode.[m [36m@@ -370,7 +373,9 @@[m [mdef make_post_page(session, post):[m return 51, 'Not found'[m page += f'=> /help/deleted-post ๐ Comment on a deleted post (ID:{post_id})\n\n'[m page += session.render_post(focused_cmt)[m [31m- flair = commenter_flair(focused_cmt, post)[m [32m+[m[32m flair = commenter_flair(focused_cmt, post,[m [32m+[m[32m abbreviate=False,[m [32m+[m[32m with_context=db.get_subspace(post.subspace))[m page += f'\n=> /u/{focused_cmt.poster_name} {focused_cmt.poster_avatar} {focused_cmt.poster_name}{flair}\n'[m page += f'{focused_cmt.age()}\n'[m [m [36m@@ -449,7 +454,13 @@[m [mdef make_post_page(session, post):[m page += '\n'[m if post.tags:[m page += '### ' + post.tags + '\n'[m [31m- flair = f" [{post.poster_flair}]" if post.poster_flair else ""[m [32m+[m[32m #flair = f" [{post.poster_flair}]" if post.poster_flair else ""[m [32m+[m [32m+[m[32m flair = User.render_flair(post.poster_flair,[m [32m+[m[32m session.context,[m [32m+[m[32m user_mod=post.user in session.context_mod_ids)[m [32m+[m[32m if flair: flair = f" [{flair}]"[m [32m+[m poster_link = f'=> /u/{post.poster_name} {post.poster_avatar} {post.poster_name}{flair}\n'[m if session.is_context_tracker:[m page += f'=> /{session.context.title()} ๐ Issue #{post.issueid} in {session.context.title()}\n'[m [36m@@ -590,7 +601,7 @@[m [mdef make_post_page(session, post):[m cmt.ymd_hm(tz=session.tz, date_fmt='%b %d', time_prefix='at ') if elapsed_hours < 24 * 180 else \[m cmt.ymd_hm(tz=session.tz, time_prefix='at ')[m [m [31m- cmt_flair = commenter_flair(cmt, post)[m [32m+[m[32m cmt_flair = commenter_flair(cmt, post, abbreviate=True)[m if not session.is_archive:[m src = f'=> /u/{cmt.poster_name}/{cmt.id} {cmt.poster_avatar} {cmt.poster_name}{cmt_flair} ยท {comment_age}:\n'[m else:[m [36m@@ -715,13 +726,14 @@[m [mdef make_feed_page(session):[m elif not context:[m topinfo += f"{session.bubble.site_info if session.user else session.bubble.site_info_nouser}\n"[m else:[m [31m- if c_user and (c_user.info or c_user.url):[m [32m+[m[32m if c_user and (c_user.info or c_user.url or c_user.flair):[m if c_user.info:[m topinfo += c_user.info + '\n'[m [31m- if c_user.flair:[m [31m- topinfo += f"[{c_user.flair}]\n"[m if c_user.url:[m topinfo += f'=> {c_user.url}\n'[m [32m+[m[32m if c_user.flair:[m [32m+[m[32m flair = User.render_flair(c_user.flair, context=None, long_form=True, db=session.db)[m [32m+[m[32m topinfo += f'\n{flair}'[m elif context:[m if context.info:[m topinfo += context.info + '\n'[m [1mdiff --git a/model.py b/model.py[m [1mindex e839ec4..1330c77 100644[m [1m--- a/model.py[m [1m+++ b/model.py[m [36m@@ -233,6 +233,15 @@[m [mclass User:[m # Roles:[m BASIC, ADMIN, LIMITED = range(3)[m [m [32m+[m[32m # Flair types:[m [32m+[m[32m FLAIRS = {[m [32m+[m[32m 'โก': 'Self description',[m [32m+[m[32m '๐๏ธ': 'Absence',[m [32m+[m[32m 'โ๏ธ': 'Writing style',[m [32m+[m[32m '๐ฃ๏ธ': 'Interaction style',[m [32m+[m[32m '๐': 'Note from moderator',[m [32m+[m[32m }[m [32m+[m # Sort modes:[m SORT_POST_RECENT = 'r'[m SORT_POST_HOTNESS = 'h'[m [36m@@ -289,6 +298,90 @@[m [mclass User:[m [m return None[m [m [32m+[m[32m def render_flair(user_flair, context, abbreviate=False, long_form=False, db=None,[m [32m+[m[32m user_mod=False, user_op=False):[m [32m+[m[32m """[m [32m+[m[32m Arguments:[m [32m+[m[32m db (Database): database object for looking up subspace names[m [32m+[m[32m in the long form.[m [32m+[m[32m context (Subspace): where the flair is being shown. If None,[m [32m+[m[32m the flair is being shown in the home feed.[m [32m+[m[32m abbreviate (bool): user-provided text is omitted and only[m [32m+[m[32m icons are shown.[m [32m+[m[32m long_form (bool): a description of the flair type is included[m [32m+[m[32m and the output is formatted onto multiple lines instead[m [32m+[m[32m of being a single line.[m [32m+[m[32m """[m [32m+[m[32m if not user_flair.strip():[m [32m+[m[32m return ''[m [32m+[m [32m+[m[32m out = ''[m [32m+[m [32m+[m[32m for flair in user_flair.split('\n'):[m [32m+[m[32m pos = flair.find(':')[m [32m+[m[32m scope = flair[:pos].strip() if pos >= 0 else None[m [32m+[m[32m is_admin_assigned = (scope and scope.startswith('*'))[m [32m+[m[32m if is_admin_assigned:[m [32m+[m[32m scope = scope[1:][m [32m+[m[32m label = flair[pos + 1:].strip() if pos >= 0 else flair[m [32m+[m[32m icon = ''[m [32m+[m[32m if len(label):[m [32m+[m[32m for key in User.FLAIRS:[m [32m+[m[32m if label.startswith(key):[m [32m+[m[32m icon = key[m [32m+[m[32m label = label[len(key):].strip()[m [32m+[m[32m break[m [32m+[m [32m+[m[32m #print(user_flair, icon, label, scope)[m [32m+[m [32m+[m[32m has_abbrev = False[m [32m+[m [32m+[m[32m if long_form:[m [32m+[m[32m # Show everything in the long form.[m [32m+[m[32m if icon:[m [32m+[m[32m out += icon + ' '[m [32m+[m[32m if scope:[m [32m+[m[32m subspace = db.get_subspace(id=abs(int(scope)))[m [32m+[m[32m if subspace:[m [32m+[m[32m scope = f" (in {subspace.title()})"[m [32m+[m[32m else:[m [32m+[m[32m scope = " (in a deleted subspace)"[m [32m+[m[32m else:[m [32m+[m[32m scope = ''[m [32m+[m[32m if icon:[m [32m+[m[32m out += f"{User.FLAIRS[icon]}: "[m [32m+[m[32m elif not scope and is_admin_assigned:[m [32m+[m[32m out += '๐ Assigned flair: '[m [32m+[m[32m else:[m [32m+[m[32m out += '๐ Personal flair: '[m [32m+[m[32m out += f"{label}{scope}{' (set by admin)' if is_admin_assigned else ''}\n"[m [32m+[m [32m+[m[32m elif not scope or (context and int(scope) == context.id):[m [32m+[m[32m # A global flair is displayed everywhere, otherwise the scope must match[m [32m+[m[32m # current context.[m [32m+[m[32m if len(out): out += ' ' if abbreviate else ', '[m [32m+[m[32m out += icon[m [32m+[m[32m if not abbreviate or not scope:[m [32m+[m[32m if len(out): out += ' '[m [32m+[m[32m out += label[m [32m+[m[32m elif not icon:[m [32m+[m[32m has_abbrev = True[m [32m+[m [32m+[m[32m if not long_form:[m [32m+[m[32m if user_op or user_mod:[m [32m+[m[32m if len(out):[m [32m+[m[32m out = ', ' + out[m [32m+[m[32m if user_op and user_mod:[m [32m+[m[32m out = 'OP/mod' + out[m [32m+[m[32m elif user_op:[m [32m+[m[32m out = 'OP' + out[m [32m+[m[32m elif user_mod:[m [32m+[m[32m out = 'mod' + out[m [32m+[m[32m if has_abbrev:[m [32m+[m[32m out += '...'[m [32m+[m [32m+[m[32m return out[m [32m+[m [m class Subspace:[m OMIT_FROM_ALL_FLAG = 0x1[m [36m@@ -500,7 +593,7 @@[m [mclass Database:[m db.execute("""CREATE TABLE IF NOT EXISTS users ([m id INT PRIMARY KEY AUTO_INCREMENT,[m name VARCHAR(30) UNIQUE,[m [31m- flair VARCHAR(30) DEFAULT '',[m [32m+[m[32m flair VARCHAR(1000) DEFAULT '',[m info VARCHAR(1000) DEFAULT '',[m url VARCHAR(1000) DEFAULT '',[m recovery VARCHAR(1000) DEFAULT '',[m
text/gemini; charset=utf-8
This content has been proxied by September (ba2dc).