forked from rDrama/rDrama
1
0
Fork 0

walls: remove duplication among routes/templates (#52)

removes a bunch of duplicated code in commenting and userpages

Co-authored-by: justcool393 <justcool393@gmail.com>
Reviewed-on: rDrama/rDrama#52
Co-authored-by: justcool393 <justcool393@noreply.fsdfsd.net>
Co-committed-by: justcool393 <justcool393@noreply.fsdfsd.net>
master
justcool393 2022-12-09 03:35:28 +00:00 committed by Snakes
parent 3e8c98494f
commit f848f68799
15 changed files with 780 additions and 1251 deletions

View File

@ -50,7 +50,7 @@ function getSelectionTextHtml() {
return html; return html;
} }
function ToggleReplyBox(id) { function toggleReplyBox(id) {
const element = document.getElementById(id); const element = document.getElementById(id);
const textarea = element.getElementsByTagName('textarea')[0] const textarea = element.getElementsByTagName('textarea')[0]
element.classList.toggle('d-none') element.classList.toggle('d-none')
@ -142,7 +142,7 @@ function post_reply(id){
document.getElementById('reply-form-body-'+id).value = '' document.getElementById('reply-form-body-'+id).value = ''
document.getElementById('message-reply-'+id).innerHTML = '' document.getElementById('message-reply-'+id).innerHTML = ''
ToggleReplyBox('reply-message-'+id) toggleReplyBox('reply-message-'+id)
document.getElementById('file-upload').value = null; document.getElementById('file-upload').value = null;
} else { } else {
showToast(false, getMessageFromJsonData(false, data)); showToast(false, getMessageFromJsonData(false, data));
@ -193,7 +193,7 @@ function comment_edit(id){
xhr[0].send(xhr[1]); xhr[0].send(xhr[1]);
} }
function post_comment(fullname, wall_user_id, hide){ function postComment(fullname, hide){
const btn = document.getElementById('save-reply-to-'+fullname) const btn = document.getElementById('save-reply-to-'+fullname)
btn.disabled = true btn.disabled = true
btn.classList.add('disabled'); btn.classList.add('disabled');
@ -202,7 +202,6 @@ function post_comment(fullname, wall_user_id, hide){
form.append('formkey', formkey()); form.append('formkey', formkey());
form.append('parent_fullname', fullname); form.append('parent_fullname', fullname);
form.append('submission', document.getElementById('reply-form-submission-'+fullname).value);
form.append('body', document.getElementById('reply-form-body-'+fullname).value); form.append('body', document.getElementById('reply-form-body-'+fullname).value);
try { try {
@ -212,8 +211,7 @@ function post_comment(fullname, wall_user_id, hide){
catch(e) {} catch(e) {}
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
if (wall_user_id == 'None') url = "/comment" url = '/comments/';
else url = '/wall_comment'
xhr.open("post", url); xhr.open("post", url);
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
xhr.onload=function(){ xhr.onload=function(){

View File

@ -21,6 +21,7 @@ from files.helpers.sanitize import *
from files.helpers.settings import get_setting from files.helpers.settings import get_setting
from files.helpers.slots import check_slots_command from files.helpers.slots import check_slots_command
post_target_type = Union[Submission, User]
def _archiveorg(url): def _archiveorg(url):
headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
@ -43,7 +44,7 @@ def archive_url(url):
url = url.replace('https://instagram.com/', 'https://imginn.com/') url = url.replace('https://instagram.com/', 'https://imginn.com/')
gevent.spawn(_archiveorg, url) gevent.spawn(_archiveorg, url)
def execute_snappy(post, v): def execute_snappy(post:Submission, v:User):
snappy = get_account(SNAPPY_ID) snappy = get_account(SNAPPY_ID)
if post.sub == 'dankchristianmemes' or post.sub == 'truth': if post.sub == 'dankchristianmemes' or post.sub == 'truth':
@ -199,10 +200,13 @@ def execute_snappy(post, v):
post.comment_count += 1 post.comment_count += 1
post.replies = [c] post.replies = [c]
def execute_zozbot(c, level, parent_submission, v): def execute_zozbot(c:Comment, level:int, post_target:post_target_type, v):
if SITE_NAME != 'rDrama': return
posting_to_submission = isinstance(post_target, Submission)
if random.random() >= 0.001: return if random.random() >= 0.001: return
c2 = Comment(author_id=ZOZBOT_ID, c2 = Comment(author_id=ZOZBOT_ID,
parent_submission=parent_submission, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
parent_comment_id=c.id, parent_comment_id=c.id,
level=level+1, level=level+1,
is_bot=True, is_bot=True,
@ -219,7 +223,8 @@ def execute_zozbot(c, level, parent_submission, v):
g.db.add(n) g.db.add(n)
c3 = Comment(author_id=ZOZBOT_ID, c3 = Comment(author_id=ZOZBOT_ID,
parent_submission=parent_submission, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
parent_comment_id=c2.id, parent_comment_id=c2.id,
level=level+2, level=level+2,
is_bot=True, is_bot=True,
@ -235,7 +240,8 @@ def execute_zozbot(c, level, parent_submission, v):
c4 = Comment(author_id=ZOZBOT_ID, c4 = Comment(author_id=ZOZBOT_ID,
parent_submission=parent_submission, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
parent_comment_id=c3.id, parent_comment_id=c3.id,
level=level+3, level=level+3,
is_bot=True, is_bot=True,
@ -253,7 +259,9 @@ def execute_zozbot(c, level, parent_submission, v):
zozbot.pay_account('coins', 1) zozbot.pay_account('coins', 1)
g.db.add(zozbot) g.db.add(zozbot)
def execute_longpostbot(c, level, body, body_html, parent_submission, v): def execute_longpostbot(c:Comment, level:int, body, body_html, post_target:post_target_type, v:User):
if SITE_NAME != 'rDrama': return
posting_to_submission = isinstance(post_target, Submission)
if not len(c.body.split()) >= 200: return if not len(c.body.split()) >= 200: return
if "</blockquote>" in body_html: return if "</blockquote>" in body_html: return
body = random.choice(LONGPOST_REPLIES) body = random.choice(LONGPOST_REPLIES)
@ -268,7 +276,8 @@ def execute_longpostbot(c, level, body, body_html, parent_submission, v):
c.downvotes = 1 c.downvotes = 1
c2 = Comment(author_id=LONGPOSTBOT_ID, c2 = Comment(author_id=LONGPOSTBOT_ID,
parent_submission=parent_submission, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
parent_comment_id=c.id, parent_comment_id=c.id,
level=level+1, level=level+1,
is_bot=True, is_bot=True,
@ -288,9 +297,12 @@ def execute_longpostbot(c, level, body, body_html, parent_submission, v):
n = Notification(comment_id=c2.id, user_id=v.id) n = Notification(comment_id=c2.id, user_id=v.id)
g.db.add(n) g.db.add(n)
def execute_basedbot(c, level, body, parent_post, v): def execute_basedbot(c:Comment, level:int, body, post_target:post_target_type, v:User):
if SITE != "pcmemes.net": return
if not c.body.lower().startswith("based"): return
posting_to_submission = isinstance(post_target, Submission)
pill = based_regex.match(body) pill = based_regex.match(body)
if level == 1: basedguy = get_account(parent_post.author_id) if level == 1: basedguy = get_account(post_target.author_id)
else: basedguy = get_account(c.parent_comment.author_id) else: basedguy = get_account(c.parent_comment.author_id)
basedguy.basedcount += 1 basedguy.basedcount += 1
if pill: if pill:
@ -303,7 +315,8 @@ def execute_basedbot(c, level, body, parent_post, v):
body_based_html = sanitize(body2) body_based_html = sanitize(body2)
c_based = Comment(author_id=BASEDBOT_ID, c_based = Comment(author_id=BASEDBOT_ID,
parent_submission=parent_post.id, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
distinguish_level=6, distinguish_level=6,
parent_comment_id=c.id, parent_comment_id=c.id,
level=level+1, level=level+1,
@ -514,3 +527,13 @@ def process_poll_options(target:Union[Submission, Comment],
exclusive=exclusive, exclusive=exclusive,
) )
db.add(option) db.add(option)
def execute_wordle(post_target:post_target_type, c:Comment, body:str, rts:bool):
if not FEATURES['WORDLE']: return
if not "!wordle" in body: return
answer = random.choice(WORDLE_LIST)
c.wordle_result = f'_active_{answer}'
if not c.wordle_result and not rts:
post_target.comment_count += 1
g.db.add(post_target)

View File

@ -79,11 +79,12 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None):
else: template = "submission.html" else: template = "submission.html"
return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True, sub=post.subr) return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True, sub=post.subr)
@app.post("/comments/")
@app.post("/comment") @app.post("/comment")
@limiter.limit("1/second;20/minute;200/hour;1000/day") @limiter.limit("1/second;20/minute;200/hour;1000/day")
@auth_required @auth_required
@ratelimit_user("1/second;20/minute;200/hour;1000/day") @ratelimit_user("1/second;20/minute;200/hour;1000/day")
def comment(v): def comment(v:User):
if v.is_suspended: abort(403, "You can't perform this action while banned.") if v.is_suspended: abort(403, "You can't perform this action while banned.")
parent_fullname = request.values.get("parent_fullname").strip() parent_fullname = request.values.get("parent_fullname").strip()
@ -92,33 +93,43 @@ def comment(v):
parent_comment_id = None parent_comment_id = None
rts = False rts = False
if parent_fullname.startswith("p_"): post_target = None
parent = None
if parent_fullname.startswith("u_"):
parent = get_account(id, v=v)
post_target = parent
elif parent_fullname.startswith("p_"):
parent = get_post(id, v=v) parent = get_post(id, v=v)
parent_post = parent post_target = parent
if POLL_THREAD and parent.id == POLL_THREAD and v.admin_level < PERMS['POST_TO_POLL_THREAD']: abort(403) if POLL_THREAD and parent.id == POLL_THREAD and v.admin_level < PERMS['POST_TO_POLL_THREAD']: abort(403)
elif parent_fullname.startswith("c_"): elif parent_fullname.startswith("c_"):
parent = get_comment(id, v=v) parent = get_comment(id, v=v)
parent_post = get_post(parent.parent_submission, v=v) post_target = get_post(parent.parent_submission, v=v, graceful=True) or get_account(parent.wall_user_id, v=v, include_blocks=True, include_shadowbanned=False)
parent_comment_id = parent.id parent_comment_id = parent.id
if parent.author_id == v.id: rts = True if parent.author_id == v.id: rts = True
if not v.can_post_in_ghost_threads and parent_post.ghost: abort(403, f"You need {TRUESCORE_GHOST_MINIMUM} truescore to post in ghost threads") if not v.can_post_in_ghost_threads and isinstance(post_target, Submission) and post_target.ghost:
else: abort(400) abort(403, f"You need {TRUESCORE_GHOST_MINIMUM} truescore to post in ghost threads")
else: abort(404)
level = 1 if isinstance(parent, Submission) else parent.level + 1 level = 1 if isinstance(parent, (Submission, User)) else int(parent.level) + 1
sub = parent_post.sub parent_user = parent if isinstance(parent, User) else parent.author
if sub and v.exiled_from(sub): abort(403, f"You're exiled from /h/{sub}") posting_to_submission = isinstance(post_target, Submission)
if sub in ('furry','vampire','racist','femboy') and not v.client and not v.house.lower().startswith(sub):
abort(403, f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}")
if not User.can_see(v, parent): abort(404) if not User.can_see(v, parent): abort(404)
if parent.deleted_utc != 0: abort(404) if not isinstance(parent, User) and parent.deleted_utc != 0: abort(404)
if posting_to_submission:
sub = post_target.sub
if sub and v.exiled_from(sub): abort(403, f"You're exiled from /h/{sub}")
if sub in ('furry','vampire','racist','femboy') and not v.client and not v.house.lower().startswith(sub):
abort(403, f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}")
if level > COMMENT_MAX_DEPTH: abort(400, f"Max comment level is {COMMENT_MAX_DEPTH}") if level > COMMENT_MAX_DEPTH: abort(400, f"Max comment level is {COMMENT_MAX_DEPTH}")
body = sanitize_raw_body(request.values.get("body", ""), False) body = sanitize_raw_body(request.values.get("body", ""), False)
if parent_post.id not in ADMIGGER_THREADS: if not posting_to_submission or post_target.id not in ADMIGGER_THREADS:
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')): if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
abort(403, "You have to type more than 280 characters!") abort(403, "You have to type more than 280 characters!")
elif v.bird and len(body) > 140: elif v.bird and len(body) > 140:
@ -126,9 +137,9 @@ def comment(v):
if not body and not request.files.get('file'): abort(400, "You need to actually write something!") if not body and not request.files.get('file'): abort(400, "You need to actually write something!")
if v.admin_level < PERMS['POST_COMMENT_MODERATION'] and parent.author.any_block_exists(v): if not v.admin_level >= PERMS['POST_COMMENT_MODERATION'] and parent_user.any_block_exists(v):
abort(403, "You can't reply to users who have blocked you or users that you have blocked.") abort(403, "You can't reply to users who have blocked you or users that you have blocked.")
body, _, options, choices = sanitize_poll_options(v, body, False) body, _, options, choices = sanitize_poll_options(v, body, False)
if request.files.get("file") and not g.is_tor: if request.files.get("file") and not g.is_tor:
@ -139,7 +150,7 @@ def comment(v):
file.save(oldname) file.save(oldname)
image = process_image(oldname, v) image = process_image(oldname, v)
if image == "": abort(400, "Image upload failed") if image == "": abort(400, "Image upload failed")
if v.admin_level >= PERMS['SITE_SETTINGS_SIDEBARS_BANNERS_BADGES'] and level == 1: if posting_to_submission and v.admin_level >= PERMS['SITE_SETTINGS_SIDEBARS_BANNERS_BADGES']:
def process_sidebar_or_banner(type, resize=0): def process_sidebar_or_banner(type, resize=0):
li = sorted(os.listdir(f'files/assets/images/{SITE_NAME}/{type}'), li = sorted(os.listdir(f'files/assets/images/{SITE_NAME}/{type}'),
key=lambda e: int(e.split('.webp')[0]))[-1] key=lambda e: int(e.split('.webp')[0]))[-1]
@ -148,12 +159,12 @@ def comment(v):
copyfile(oldname, filename) copyfile(oldname, filename)
process_image(filename, v, resize=resize) process_image(filename, v, resize=resize)
if parent_post.id == SIDEBAR_THREAD: if post_target.id == SIDEBAR_THREAD:
process_sidebar_or_banner('sidebar', 400) process_sidebar_or_banner('sidebar', 400)
elif parent_post.id == BANNER_THREAD: elif post_target.id == BANNER_THREAD:
banner_width = 1200 if not SITE_NAME == 'PCM' else 0 banner_width = 1200 if not SITE_NAME == 'PCM' else 0
process_sidebar_or_banner('banners', banner_width) process_sidebar_or_banner('banners', banner_width)
elif parent_post.id == BADGE_THREAD: elif post_target.id == BADGE_THREAD:
try: try:
badge_def = loads(body) badge_def = loads(body)
name = badge_def["name"] name = badge_def["name"]
@ -180,48 +191,51 @@ def comment(v):
body = body.strip()[:COMMENT_BODY_LENGTH_LIMIT] body = body.strip()[:COMMENT_BODY_LENGTH_LIMIT]
if v.admin_level >= PERMS['SITE_SETTINGS_SNAPPY_QUOTES'] and parent_post.id == SNAPPY_THREAD and level == 1: if v.admin_level >= PERMS['SITE_SETTINGS_SNAPPY_QUOTES'] and posting_to_submission and post_target.id == SNAPPY_THREAD and level == 1:
with open(f"snappy_{SITE_NAME}.txt", "a", encoding="utf-8") as f: with open(f"snappy_{SITE_NAME}.txt", "a", encoding="utf-8") as f:
f.write('\n{[para]}\n' + body) f.write('\n{[para]}\n' + body)
body_for_sanitize = body body_for_sanitize = body
if v.owoify: if v.owoify: body_for_sanitize = owoify(body_for_sanitize)
body_for_sanitize = owoify(body_for_sanitize) if v.marsify: body_for_sanitize = marsify(body_for_sanitize)
if v.marsify:
body_for_sanitize = marsify(body_for_sanitize)
torture = (v.agendaposter and not v.marseyawarded and parent_post.sub != 'chudrama' and parent_post.id not in ADMIGGER_THREADS) torture = (v.agendaposter and not v.marseyawarded and post_target.sub != 'chudrama' and post_target.id not in ADMIGGER_THREADS)
body_html = sanitize(body_for_sanitize, limit_pings=5, count_marseys=not v.marsify, torture=torture) body_html = sanitize(body_for_sanitize, limit_pings=5, count_marseys=not v.marsify, torture=torture)
if parent_post.id not in ADMIGGER_THREADS and '!wordle' not in body.lower() and AGENDAPOSTER_PHRASE not in body.lower(): if post_target.id not in ADMIGGER_THREADS and '!wordle' not in body.lower() and AGENDAPOSTER_PHRASE not in body.lower():
existing = g.db.query(Comment.id).filter( existing = g.db.query(Comment.id).filter(
Comment.author_id == v.id, Comment.author_id == v.id,
Comment.deleted_utc == 0, Comment.deleted_utc == 0,
Comment.parent_comment_id == parent_comment_id, Comment.parent_comment_id == parent_comment_id,
Comment.parent_submission == parent_post.id, Comment.parent_submission == post_target.id if posting_to_submission else None,
Comment.wall_user_id == post_target.id if not posting_to_submission else None,
Comment.body_html == body_html Comment.body_html == body_html
).first() ).first()
if existing: abort(409, f"You already made that comment: /comment/{existing.id}") if existing: abort(409, f"You already made that comment: /comment/{existing.id}")
execute_antispam_comment_check(body, v)
execute_antispam_duplicate_comment_check(v, body_html)
if v.marseyawarded and posting_to_submission and post_target.id not in ADMIGGER_THREADS and marseyaward_body_regex.search(body_html):
abort(403, "You can only type marseys!")
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
is_bot = (v.client is not None is_bot = (v.client is not None
and v.id not in PRIVILEGED_USER_BOTS and v.id not in PRIVILEGED_USER_BOTS
or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID)) or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID))
execute_antispam_comment_check(body, v)
execute_antispam_duplicate_comment_check(v, body_html)
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
c = Comment(author_id=v.id, c = Comment(author_id=v.id,
parent_submission=parent_post.id, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
parent_comment_id=parent_comment_id, parent_comment_id=parent_comment_id,
level=level, level=level,
over_18=parent_post.over_18, over_18=post_target.over_18 if posting_to_submission else False,
is_bot=is_bot, is_bot=is_bot,
app_id=v.client.application.id if v.client else None, app_id=v.client.application.id if v.client else None,
body_html=body_html, body_html=body_html,
body=body, body=body,
ghost=parent_post.ghost ghost=post_target.ghost if posting_to_submission else False,
) )
c.upvotes = 1 c.upvotes = 1
@ -237,10 +251,9 @@ def comment(v):
process_poll_options(c, CommentOption, options, 0, "Poll", g.db) process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
process_poll_options(c, CommentOption, choices, 1, "Poll", g.db) process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
if SITE == 'pcmemes.net' and c.body.lower().startswith("based"): execute_basedbot(c, level, body, post_target, v)
execute_basedbot(c, level, body, parent_post, v)
if parent_post.id not in ADMIGGER_THREADS and v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and parent_post.sub != 'chudrama': if post_target.id not in ADMIGGER_THREADS and v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and post_target.sub != 'chudrama':
c.is_banned = True c.is_banned = True
c.ban_reason = "AutoJanny" c.ban_reason = "AutoJanny"
g.db.add(c) g.db.add(c)
@ -249,7 +262,8 @@ def comment(v):
body_jannied_html = AGENDAPOSTER_MSG_HTML.format(id=v.id, username=v.username, type='comment', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE) body_jannied_html = AGENDAPOSTER_MSG_HTML.format(id=v.id, username=v.username, type='comment', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE)
c_jannied = Comment(author_id=AUTOJANNY_ID, c_jannied = Comment(author_id=AUTOJANNY_ID,
parent_submission=parent_post.id, parent_submission=post_target.id if posting_to_submission else None,
wall_user_id=post_target.id if not posting_to_submission else None,
distinguish_level=6, distinguish_level=6,
parent_comment_id=c.id, parent_comment_id=c.id,
level=level+1, level=level+1,
@ -266,46 +280,41 @@ def comment(v):
n = Notification(comment_id=c_jannied.id, user_id=v.id) n = Notification(comment_id=c_jannied.id, user_id=v.id)
g.db.add(n) g.db.add(n)
if SITE_NAME == 'rDrama': execute_longpostbot(c, level, body, body_html, post_target, v)
execute_longpostbot(c, level, body, body_html, parent_post.id, v) execute_zozbot(c, level, post_target, v)
execute_zozbot(c, level, parent_post.id, v)
if not v.shadowbanned: if not v.shadowbanned:
notify_users = NOTIFY_USERS(body, v) notify_users = NOTIFY_USERS(body, v)
if c.level == 1: if c.level == 1 and posting_to_submission:
subscribers = g.db.query(Subscription.user_id).filter(Subscription.submission_id == parent_post.id, Subscription.user_id != v.id).all() subscribers = g.db.query(Subscription.user_id).filter(Subscription.submission_id == post_target.id, Subscription.user_id != v.id).all()
for x in subscribers: for x in subscribers:
notify_users.add(x[0]) notify_users.add(x[0])
if parent.author.id != v.id: if parent_user.id != v.id:
notify_users.add(parent.author.id) notify_users.add(parent_user.id)
for x in notify_users-bots: for x in notify_users-bots:
n = Notification(comment_id=c.id, user_id=x) n = Notification(comment_id=c.id, user_id=x)
g.db.add(n) g.db.add(n)
if VAPID_PUBLIC_KEY != DEFAULT_CONFIG_VALUE and parent.author.id != v.id and not v.shadowbanned: if VAPID_PUBLIC_KEY != DEFAULT_CONFIG_VALUE and parent_user.id != v.id and not v.shadowbanned:
title = f'New reply by @{c.author_name}' title = f'New reply by @{c.author_name}'
if not posting_to_submission: title = f"New comment on your wall by @{c.author_name}"
if len(c.body) > 500: notifbody = c.body[:500] + '...' if len(c.body) > PUSH_NOTIF_LIMIT: notifbody = c.body[:PUSH_NOTIF_LIMIT] + '...'
else: notifbody = c.body else: notifbody = c.body
url = f'{SITE_FULL}/comment/{c.id}?context=8&read=true#context' url = f'{SITE_FULL}/comment/{c.id}?context=8&read=true#context'
push_notif(parent.author.id, title, notifbody, url) push_notif(parent_user.id, title, notifbody, url)
vote = CommentVote(user_id=v.id, vote = CommentVote(user_id=v.id,
comment_id=c.id, comment_id=c.id,
vote_type=1, vote_type=1,
) )
g.db.add(vote) g.db.add(vote)
cache.delete_memoized(comment_idlist) cache.delete_memoized(comment_idlist)
v.comment_count = g.db.query(Comment).filter( v.comment_count = g.db.query(Comment).filter(
@ -316,20 +325,9 @@ def comment(v):
g.db.add(v) g.db.add(v)
c.voted = 1 c.voted = 1
if v.marseyawarded and parent_post.id not in ADMIGGER_THREADS and marseyaward_body_regex.search(body_html):
abort(403, "You can only type marseys!")
check_for_treasure(body, c) check_for_treasure(body, c)
execute_wordle(post_target or parent_user, c, body, rts)
if FEATURES['WORDLE'] and "!wordle" in body:
answer = random.choice(WORDLE_LIST)
c.wordle_result = f'_active_{answer}'
if not c.wordle_result and not rts:
parent_post.comment_count += 1
g.db.add(parent_post)
check_slots_command(v, v, c) check_slots_command(v, v, c)
if c.level > 5: if c.level > 5:
@ -344,216 +342,6 @@ def comment(v):
if v.client: return c.json(db=g.db) if v.client: return c.json(db=g.db)
return {"comment": render_template("comments.html", v=v, comments=[c])} return {"comment": render_template("comments.html", v=v, comments=[c])}
@app.post("/wall_comment")
@limiter.limit("1/second;20/minute;200/hour;1000/day")
@auth_required
@ratelimit_user("1/second;20/minute;200/hour;1000/day")
def wall_comment(v):
if v.is_suspended: abort(403, "You can't perform this action while banned.")
parent_fullname = request.values.get("parent_fullname").strip()
if len(parent_fullname) < 3: abort(400)
id = parent_fullname[2:]
parent_comment_id = None
if parent_fullname.startswith("u_"):
parent = get_account(id, v=v)
parent_user = parent
parent_author = parent
elif parent_fullname.startswith("c_"):
parent = get_comment(id, v=v)
if parent.deleted_utc != 0: abort(404)
parent_user = parent.wall_user
parent_comment_id = parent.id
parent_author = parent.author
else: abort(400)
level = 1 if isinstance(parent, User) else parent.level + 1
# if not User.can_see(v, parent): abort(404)
if level > COMMENT_MAX_DEPTH: abort(400, f"Max comment level is {COMMENT_MAX_DEPTH}")
body = sanitize_raw_body(request.values.get("body", ""), False)
if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')):
abort(403, "You have to type more than 280 characters!")
elif v.bird and len(body) > 140:
abort(403, "You have to type less than 140 characters!")
if not body and not request.files.get('file'):
abort(400, "You need to actually write something!")
if v.admin_level < PERMS['POST_COMMENT_MODERATION'] and parent_author.any_block_exists(v):
abort(403, "You can't reply to users who have blocked you or users that you have blocked.")
body, _, options, choices = sanitize_poll_options(v, body, False)
if request.files.get("file") and not g.is_tor:
files = request.files.getlist('file')[:4]
for file in files:
if file.content_type.startswith('image/'):
oldname = f'/images/{time.time()}'.replace('.','') + '.webp'
file.save(oldname)
image = process_image(oldname, v)
if image == "": abort(400, "Image upload failed")
body += f"\n\n![]({image})"
elif file.content_type.startswith('video/'):
body += f"\n\n{SITE_FULL}{process_video(file, v)}"
elif file.content_type.startswith('audio/'):
body += f"\n\n{SITE_FULL}{process_audio(file, v)}"
else:
abort(415)
body = body.strip()[:COMMENT_BODY_LENGTH_LIMIT]
body_for_sanitize = body
if v.owoify:
body_for_sanitize = owoify(body_for_sanitize)
if v.marsify:
body_for_sanitize = marsify(body_for_sanitize)
torture = (v.agendaposter and not v.marseyawarded)
body_html = sanitize(body_for_sanitize, limit_pings=5, count_marseys=not v.marsify, torture=torture)
if '!wordle' not in body.lower() and AGENDAPOSTER_PHRASE not in body.lower():
existing = g.db.query(Comment.id).filter(
Comment.author_id == v.id,
Comment.deleted_utc == 0,
Comment.parent_comment_id == parent_comment_id,
Comment.parent_submission == None,
Comment.wall_user_id == parent_user.id,
Comment.body_html == body_html
).first()
if existing: abort(409, f"You already made that comment: /comment/{existing.id}")
is_bot = (v.client is not None
and v.id not in PRIVILEGED_USER_BOTS
or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID))
execute_antispam_comment_check(body, v)
execute_antispam_duplicate_comment_check(v, body_html)
if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400)
c = Comment(author_id=v.id,
wall_user_id=parent_user.id,
parent_comment_id=parent_comment_id,
level=level,
is_bot=is_bot,
app_id=v.client.application.id if v.client else None,
body_html=body_html,
body=body,
)
c.upvotes = 1
g.db.add(c)
g.db.flush()
execute_blackjack(v, c, c.body, "comment")
execute_under_siege(v, c, c.body, "comment")
if c.level == 1: c.top_comment_id = c.id
else: c.top_comment_id = parent.top_comment_id
process_poll_options(c, CommentOption, options, 0, "Poll", g.db)
process_poll_options(c, CommentOption, choices, 1, "Poll", g.db)
if v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower():
c.is_banned = True
c.ban_reason = "AutoJanny"
g.db.add(c)
body = AGENDAPOSTER_MSG.format(username=v.username, type='comment', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE)
body_jannied_html = AGENDAPOSTER_MSG_HTML.format(id=v.id, username=v.username, type='comment', AGENDAPOSTER_PHRASE=AGENDAPOSTER_PHRASE)
c_jannied = Comment(author_id=AUTOJANNY_ID,
parent_submission=None,
wall_user_id=parent_user.id,
distinguish_level=6,
parent_comment_id=c.id,
level=level+1,
is_bot=True,
body=body,
body_html=body_jannied_html,
top_comment_id=c.top_comment_id,
)
g.db.add(c_jannied)
g.db.flush()
n = Notification(comment_id=c_jannied.id, user_id=v.id)
g.db.add(n)
if not v.shadowbanned:
notify_users = NOTIFY_USERS(body, v)
if parent_author.id != v.id:
notify_users.add(parent_author.id)
for x in notify_users-bots:
n = Notification(comment_id=c.id, user_id=x)
g.db.add(n)
if VAPID_PUBLIC_KEY != DEFAULT_CONFIG_VALUE and parent_author.id != v.id and not v.shadowbanned:
if parent_author.id == c.wall_user_id:
title = f"New comment on your profile wall by @{c.author_name}"
else:
title = f"New reply by @{c.author_name}"
if len(c.body) > 500: notifbody = c.body[:500] + '...'
else: notifbody = c.body
url = f'{SITE_FULL}/@{c.wall_user.username}/wall/comment/{c.id}?context=8&read=true#context'
push_notif(parent_author.id, title, notifbody, url)
vote = CommentVote(user_id=v.id,
comment_id=c.id,
vote_type=1,
)
g.db.add(vote)
v.comment_count = g.db.query(Comment).filter(
Comment.author_id == v.id,
or_(Comment.parent_submission != None, Comment.wall_user_id != None),
Comment.deleted_utc == 0
).count()
g.db.add(v)
c.voted = 1
if v.marseyawarded and marseyaward_body_regex.search(body_html):
abort(403, "You can only type marseys!")
check_for_treasure(body, c)
if FEATURES['WORDLE'] and "!wordle" in body:
answer = random.choice(WORDLE_LIST)
c.wordle_result = f'_active_{answer}'
check_slots_command(v, v, c)
if c.level > 5:
n = g.db.query(Notification).filter_by(
comment_id=c.parent_comment.parent_comment.parent_comment.parent_comment_id,
user_id=c.parent_comment.author_id,
).one_or_none()
if n: g.db.delete(n)
g.db.flush()
cache.delete_memoized(comment_idlist)
if v.client: return c.json(db=g.db)
return {"comment": render_template("comments.html", v=v, comments=[c])}
@app.post("/edit_comment/<cid>") @app.post("/edit_comment/<cid>")
@limiter.limit("1/second;10/minute;100/hour;200/day") @limiter.limit("1/second;10/minute;100/hour;200/day")
@is_not_permabanned @is_not_permabanned

View File

@ -538,7 +538,7 @@ def messagereply(v:User):
execute_blackjack(v, c, c.body_html, 'message') execute_blackjack(v, c, c.body_html, 'message')
execute_under_siege(v, c, c.body_html, 'message') execute_under_siege(v, c, c.body_html, 'message')
if user_id and user_id not in (v.id, 2, bots): if user_id and user_id not in (v.id, MODMAIL_ID, bots):
notif = g.db.query(Notification).filter_by(comment_id=c.id, user_id=user_id).one_or_none() notif = g.db.query(Notification).filter_by(comment_id=c.id, user_id=user_id).one_or_none()
if not notif: if not notif:
notif = Notification(comment_id=c.id, user_id=user_id) notif = Notification(comment_id=c.id, user_id=user_id)
@ -878,7 +878,7 @@ def u_username(v:Optional[User]=None, username:str):
if (v and v.client) or request.path.endswith(".json"): if (v and v.client) or request.path.endswith(".json"):
return {"data": [x.json(g.db) for x in listing]} return {"data": [x.json(g.db) for x in listing]}
return render_template("userpage/userpage.html", return render_template("userpage/submissions.html",
unban=u.unban_string, unban=u.unban_string,
u=u, u=u,
v=v, v=v,
@ -892,7 +892,7 @@ def u_username(v:Optional[User]=None, username:str):
if (v and v.client) or request.path.endswith(".json"): if (v and v.client) or request.path.endswith(".json"):
return {"data": [x.json(g.db) for x in listing]} return {"data": [x.json(g.db) for x in listing]}
return render_template("userpage/userpage.html", return render_template("userpage/submissions.html",
u=u, u=u,
v=v, v=v,
listing=listing, listing=listing,
@ -1126,7 +1126,7 @@ def saved_posts(v:User, username):
try: page = max(1, int(request.values.get("page", 1))) try: page = max(1, int(request.values.get("page", 1)))
except: abort(400, "Invalid page input!") except: abort(400, "Invalid page input!")
return get_saves_and_subscribes(v, "userpage/userpage.html", SaveRelationship, page, False) return get_saves_and_subscribes(v, "userpage/submissions.html", SaveRelationship, page, False)
@app.get("/@<username>/saved/comments") @app.get("/@<username>/saved/comments")
@auth_required @auth_required
@ -1142,7 +1142,7 @@ def subscribed_posts(v:User, username):
try: page = max(1, int(request.values.get("page", 1))) try: page = max(1, int(request.values.get("page", 1)))
except: abort(400, "Invalid page input!") except: abort(400, "Invalid page input!")
return get_saves_and_subscribes(v, "userpage/userpage.html", Subscription, page, False) return get_saves_and_subscribes(v, "userpage/submissions.html", Subscription, page, False)
@app.post("/fp/<fp>") @app.post("/fp/<fp>")
@auth_required @auth_required

View File

@ -275,6 +275,7 @@
{% if c.parent_submission or c.wall_user_id %} {% if c.parent_submission or c.wall_user_id %}
{% if v and v.id==c.author_id %} {% if v and v.id==c.author_id %}
<div id="comment-edit-{{c.id}}" class="d-none comment-write collapsed child"> <div id="comment-edit-{{c.id}}" class="d-none comment-write collapsed child">
<form id="comment-edit-form-{{c.id}}" action="/edit_comment/{{c.id}}" method="post" enctype="multipart/form-data"> <form id="comment-edit-form-{{c.id}}" action="/edit_comment/{{c.id}}" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}"> <input type="hidden" name="formkey" value="{{v|formkey}}">
@ -301,9 +302,6 @@
</div> </div>
{% endif %} {% endif %}
<div id="comment-{{c.id}}-actions" class="comment-actions{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}"> <div id="comment-{{c.id}}-actions" class="comment-actions{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
<div class="d-md-none mt-1"> <div class="d-md-none mt-1">
<div class="post-actions"> <div class="post-actions">
<ul class="list-inline text-right d-flex"> <ul class="list-inline text-right d-flex">
@ -316,7 +314,7 @@
</li> </li>
{% if v and not c.deleted_utc %} {% if v and not c.deleted_utc %}
<button type="button" class="list-inline-item mr-3 btn nobackground" onclick="ToggleReplyBox('reply-to-{{c.id}}')"><i class="fas fa-reply" style="margin-top:0.35rem"></i></button> <button type="button" class="list-inline-item mr-3 btn nobackground" onclick="toggleReplyBox('reply-to-{{c.fullname}}')"><i class="fas fa-reply" style="margin-top:0.35rem"></i></button>
{% endif %} {% endif %}
<li class="list-inline-item"> <li class="list-inline-item">
@ -365,15 +363,6 @@
</ul> </ul>
</div> </div>
</div> </div>
<ul class="d-none d-md-flex list-inline text-right text-md-left"><li> <ul class="d-none d-md-flex list-inline text-right text-md-left"><li>
{% if v and (request.path.startswith('/@') and not wall) and v.admin_level < PERMS['VIEW_VOTE_BUTTONS_ON_USER_PAGE'] %} {% if v and (request.path.startswith('/@') and not wall) and v.admin_level < PERMS['VIEW_VOTE_BUTTONS_ON_USER_PAGE'] %}
@ -414,7 +403,7 @@
{% if v %} {% if v %}
{% if not c.deleted_utc %} {% if not c.deleted_utc %}
<button type="button" class="btn caction py-0 nobackground px-1 text-muted" onclick="ToggleReplyBox('reply-to-{{c.id}}')"><i class="fas fa-reply" aria-hidden="true"></i>Reply</button> <button type="button" class="btn caction py-0 nobackground px-1 text-muted" onclick="toggleReplyBox('reply-to-{{c.fullname}}')"><i class="fas fa-reply" aria-hidden="true"></i>Reply</button>
{% endif %} {% endif %}
<button type="button" class="btn caction py-0 nobackground px-1 text-muted" data-bs-toggle="modal" data-bs-target="#reportCommentModal" onclick="report_commentModal('{{c.id}}','{{c.author_name}}',)"><i class="fas fa-flag fa-fw"></i>Report</button> <button type="button" class="btn caction py-0 nobackground px-1 text-muted" data-bs-toggle="modal" data-bs-target="#reportCommentModal" onclick="report_commentModal('{{c.id}}','{{c.author_name}}',)"><i class="fas fa-flag fa-fw"></i>Report</button>
@ -517,39 +506,7 @@
{% if v and v.id != c.author_id and c.body %} {% if v and v.id != c.author_id and c.body %}
<div autocomplete="off" class="markdown d-none card border my-2 p-3 comment-box form-control rounded" id="markdown-{{c.id}}" readonly>{{c.body.strip()}}</div> <div autocomplete="off" class="markdown d-none card border my-2 p-3 comment-box form-control rounded" id="markdown-{{c.id}}" readonly>{{c.body.strip()}}</div>
{% endif %} {% endif %}
{{macros.comment_reply_box(c.fullname, "reply-to-" + c.fullname, "d-none", "collapsed child", 'reply-to-' + c.fullname, true, '/comments/')}}
<div id="reply-to-{{c.id}}" class="d-none">
<div id="comment-form-space-{{c.fullname}}" class="comment-write collapsed child">
<form id="reply-to-c_{{c.id}}" action="/comment" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<input type="hidden" name="parent_fullname" value="{{c.fullname}}">
<input autocomplete="off" id="reply-form-submission-{{c.fullname}}" type="hidden" name="submission" value="{{c.post.id}}">
<textarea required autocomplete="off" {% if v.longpost %}minlength="280"{% else %}minlength="1"{% endif %} maxlength="{% if v.bird %}140{% else %}10000{% endif %}" data-preview="form-preview-{{c.fullname}}" oninput="markdown(this);charLimit('reply-form-body-{{c.fullname}}','charcount-{{c.fullname}}')" id="reply-form-body-{{c.fullname}}" data-fullname="{{c.fullname}}" name="body" form="reply-to-c_{{c.id}}" class="comment-box form-control rounded" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
<div class="text-small font-weight-bold mt-1" id="charcount-{{c.fullname}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
<div class="comment-format" id="comment-format-bar-{{c.id}}">
<label class="btn btn-secondary format m-0" for="gif-reply-btn-{{c.fullname}}" onclick="commentForm('reply-form-body-{{c.fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">
<span id="gif-reply-btn-{{c.fullname}}" class="font-weight-bolder text-uppercase">GIF</span>
</label>
&nbsp;
<label class="btn btn-secondary format m-0" for="gif-reply-btn-{{c.fullname}}" onclick="loadEmojis('reply-form-body-{{c.fullname}}')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji">
<i id="emoji-reply-btn-{{c.fullname}}" class="fas fa-smile-beam"></i>
</label>
&nbsp;
<label class="btn btn-secondary format m-0" for="file-upload-reply-{{c.fullname}}">
<div id="filename-show-reply-{{c.fullname}}"><i class="fas fa-file"></i></div>
<input autocomplete="off" id="file-upload-reply-{{c.fullname}}" accept="image/*, video/*, audio/*" type="file" multiple="multiple" name="file" {% if g.is_tor %}disabled{% endif %} onchange="changename('filename-show-reply-{{c.fullname}}','file-upload-reply-{{c.fullname}}')" hidden>
</label>
</div>
<button type="button" id="save-reply-to-{{c.fullname}}" class="btn btn-primary ml-2 fl-r commentmob" onclick="post_comment('{{c.fullname}}', '{{c.wall_user_id}}', 'reply-to-{{c.id}}');remove_dialog()">Comment</button>
<button type="button" onclick="document.getElementById('reply-to-{{c.id}}').classList.add('d-none');remove_dialog()" class="btn btn-link text-muted ml-auto cancel-form fl-r commentmob">Cancel</button>
</form>
<div id="form-preview-{{c.fullname}}" class="preview mb-3 mt-5"></div>
<div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div>
</div>
</div>
{% if request.path.startswith('/transfers') %} {% if request.path.startswith('/transfers') %}
<a href="{{c.log_link}}"><i class="far fa-link ml-1 text-muted"></i></a> <a href="{{c.log_link}}"><i class="far fa-link ml-1 text-muted"></i></a>
@ -578,9 +535,10 @@
<button type="button" class="btn btn-primary mr-3 {% if not c.author.is_muted %}d-none{% endif %}" id="unmute-user-{{c.id}}" onclick="postToastSwitch(this,'/unmute_user/{{c.author.id}}','mute-user-{{c.id}}','unmute-user-{{c.id}}','d-none')">Unmute</button> <button type="button" class="btn btn-primary mr-3 {% if not c.author.is_muted %}d-none{% endif %}" id="unmute-user-{{c.id}}" onclick="postToastSwitch(this,'/unmute_user/{{c.author.id}}','mute-user-{{c.id}}','unmute-user-{{c.id}}','d-none')">Unmute</button>
{% endif %} {% endif %}
<button type="button" class="btn btn-primary nobackground" onclick="ToggleReplyBox('reply-message-{{c.id}}')">Reply</button> <button type="button" class="btn btn-primary nobackground" onclick="toggleReplyBox('reply-message-{{c.fullname}}')">Reply</button>
<div id="reply-message-{{c.id}}" class="d-none"> {# TODO: swap to comment_reply_box macro #}
<div id="reply-message-{{c.fullname}}" class="d-none">
<div id="comment-form-space-{{c.id}}" class="comment-write collapsed child"> <div id="comment-form-space-{{c.id}}" class="comment-write collapsed child">
<form id="reply-to-message-{{c.id}}" action="/reply" method="post" class="input-group" enctype="multipart/form-data"> <form id="reply-to-message-{{c.id}}" action="/reply" method="post" class="input-group" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}"> <input type="hidden" name="formkey" value="{{v|formkey}}">

View File

@ -194,7 +194,6 @@
<div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div> <div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div>
</div> </div>
{% endif %} {% endif %}
{% if p.embed_url and "http" not in p.embed_url and "<" not in p.embed_url %} {% if p.embed_url and "http" not in p.embed_url and "<" not in p.embed_url %}
<div id="crosspost-embed" class="mb-3"> <div id="crosspost-embed" class="mb-3">
<div class="row no-gutters"> <div class="row no-gutters">
@ -209,7 +208,6 @@
<div class="d-none d-md-flex justify-content-between align-items-center mt-2"> <div class="d-none d-md-flex justify-content-between align-items-center mt-2">
<div class="post-actions mt-2"> <div class="post-actions mt-2">
<ul class="list-inline text-right d-flex"> <ul class="list-inline text-right d-flex">
<a class="list-inline-item" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %} href="{{p.permalink}}"> <a class="list-inline-item" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %} href="{{p.permalink}}">
<i class="fas fa-comment-dots mr-2"></i>{{p.comment_count}} <i class="fas fa-comment-dots mr-2"></i>{{p.comment_count}}
<span class="text-info d-none {{p.id}}-new-comments"></span> <span class="text-info d-none {{p.id}}-new-comments"></span>
@ -226,13 +224,10 @@
{% include 'post_actions.html' %} {% include 'post_actions.html' %}
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
{% if v %} {% if v %}
<div id="voting" class="voting d-mob-none mb-auto"> <div id="voting" class="voting d-mob-none mb-auto">
<div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div> <div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div>
@ -248,31 +243,21 @@
<span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}style="cursor: default">{{score}}</span> <span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}style="cursor: default">{{score}}</span>
<div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.full_path | urlencode}}';"></div> <div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.full_path | urlencode}}';"></div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row mb-3 d-md-none"> <div class="row mb-3 d-md-none">
<div class="col-12"> <div class="col-12">
<div class="post-actions"> <div class="post-actions">
<ul class="list-inline text-right d-flex"> <ul class="list-inline text-right d-flex">
<li class="list-inline-item mr-auto"> <li class="list-inline-item mr-auto">
<a href="{{p.permalink}}"> <a href="{{p.permalink}}">
<i class="fas fa-comment-dots mr-2"></i>{{p.comment_count}} <i class="fas fa-comment-dots mr-2"></i>{{p.comment_count}}
<span class="text-info d-none {{p.id}}-new-comments"></span> <span class="text-info d-none {{p.id}}-new-comments"></span>
</a> </a>
{% if v and v.admin_level >= PERMS['POST_COMMENT_MODERATION_TOOLS_VISIBLE'] %} {% if v and v.admin_level >= PERMS['POST_COMMENT_MODERATION_TOOLS_VISIBLE'] %}
<button type="button" class="ml-2" data-bs-toggle="modal" data-bs-target="#adminModal-{{p.id}}"> <button type="button" class="ml-2" data-bs-toggle="modal" data-bs-target="#adminModal-{{p.id}}">
<i class="fas fa-broom"></i> <i class="fas fa-broom"></i>
@ -341,49 +326,7 @@
</div> </div>
{% if not p.deleted_utc %} {% if not p.deleted_utc %}
{% if v %} {{macros.comment_reply_box(p.fullname, 'comment-reply-' + p.fullname, '', 'mb-3', '', true, '/comments/')}}
<div id="comment-form-space-{{p.fullname}}" class="comment-write mb-3">
<form id="reply-to-{{p.fullname}}" action="/comment" method="post">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<input type="hidden" name="parent_fullname" value="p_{{p.id}}">
<input autocomplete="off" id="reply-form-submission-{{p.fullname}}" type="hidden" name="submission" value="{{p.id}}">
<textarea required autocomplete="off" {% if not (p and p.id in ADMIGGER_THREADS) %}{% if v.longpost %}minlength="280"{% elif v.bird %}maxlength="140"{% endif %}{% endif %} minlength="1" maxlength="10000" data-preview="form-preview-{{p.fullname}}" oninput="markdown(this);charLimit('reply-form-body-{{p.fullname}}','charcount-{{p.fullname}}')" id="reply-form-body-{{p.fullname}}" data-fullname="{{p.fullname}}" class="comment-box form-control rounded" name="body" form="reply-to-{{p.fullname}}" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
<div class="text-small font-weight-bold mt-1" id="charcount-{{p.fullname}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
<div class="comment-format">
<label class="btn btn-secondary format d-inline-block m-0" for="gif-reply-btn-{{p.fullname}}">
<span id="gif-reply-btn-{{p.fullname}}" class="font-weight-bolder text-uppercase" onclick="commentForm('reply-form-body-{{p.fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span>
</label>
&nbsp;
<div onclick="loadEmojis('reply-form-body-{{p.fullname}}')" class="btn btn-secondary format d-inline-block m-0" id="emoji-reply-btn-{{p.fullname}}" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-smile-beam"></i></div>
&nbsp;
<label class="format btn btn-secondary m-0 ml-1 {% if v %}d-inline-block{% else %}d-none{% endif %}" for="file-upload-reply-{{p.fullname}}">
<div id="filename-show-reply-{{p.fullname}}"><i class="fas fa-file"></i></div>
<input autocomplete="off" id="file-upload-reply-{{p.fullname}}" accept="image/*, video/*, audio/*" type="file" multiple="multiple" name="file" {% if g.is_tor %}disabled{% endif %} onchange="changename('filename-show-reply-{{p.fullname}}','file-upload-reply-{{p.fullname}}')" hidden>
</label>
</div>
<button type="button" id="save-reply-to-{{p.fullname}}" form="reply-to-{{p.fullname}}" class="btn btn-primary text-whitebtn ml-auto fl-r" onclick="post_comment('{{p.fullname}}', 'None');remove_dialog()">Comment</button>
</form>
<div id="form-preview-{{p.fullname}}" class="preview mb-3 mt-5"></div>
<div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div>
</div>
{% else %}
<div class="comment-write mb-3">
<textarea autocomplete="off" maxlength="10000" class="comment-box form-control rounded" name="body" aria-label="With textarea" placeholder="Add your comment..." rows="3" onclick="location.href='/login?redirect={{request.full_path | urlencode}}';"></textarea>
</div>
<div class="card border-0 mt-4">
<div class="card-body">
<h5 class="card-title">Jump in the discussion.</h5>
<p class="card-text">No email address required.</p>
<div>
<a href="/signup?redirect={{request.full_path | urlencode}}" class="btn btn-primary">Sign up</a>
<a href="/login?redirect={{request.full_path | urlencode}}" class="btn btn-link text-muted">Sign in</a>
</div>
</div>
</div>
{% endif %}
{% endif %} {% endif %}
{% if comment_info %} {% if comment_info %}

View File

@ -0,0 +1,505 @@
{% import 'userpage/admintools.html' as userpage_admintools with context %}
{% set hats_total = u.hats_owned_proportion_display[1] if u else 0 %}
{% set hats_owned_percent = u.hats_owned_proportion_display[0] if u else '' %}
{% block desktopUserBanner %}
<div class="row d-mob-none">
<div class="col px-0">
<div class="jumbotron jumbotron-fluid jumbotron-guild d-mob-none" {% if FEATURES['USERS_PROFILE_BANNER'] %}style="background-image: url({{u.banner_url}})"{% endif %}>
<div class="jumbotron-overlay"></div>
<div class="w-100 my-3">
<div class="container-fluid nobackground">
<div class="d-md-flex text-center text-md-left">
<div id="profile--pfp" {% if u.hat_active %}class="profile--pfp--hat hat"{% endif %}>
<a rel="nofollow noopener" href="{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}" class="profile-pic-100-wrapper">
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" loading="lazy" src="{{u.profile_url}}" class="profile-pic profile-pic-100 mb-5">
{% if u.hat_active -%}
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" class="profile-pic-100-hat hat" loading="lazy" src="{{u.hat_active}}?h=7" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.hat_tooltip(v)}}">
{%- endif %}
</a>
</div>
<div id="profilestuff" class="ml-3 w-100">
{{userpage_admintools.userBanBlock('desktop')}}
<div class="d-flex align-items-center mt-1 mb-2">
<h3 class="font-weight-bolder my-0 mr-2" id="profile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.user_name}}</span></h3>
{% if u.username != u.original_username %}
<span id="profile--origname">
<i class="fas fa-user-tag text-info align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Original Username: @{{u.original_username}}"></i>
</span>
{% endif %}
{% if FEATURES['PATRON_ICONS'] and u.patron %}
<img loading="lazy" class="ml-3" src="/i/{{SITE_NAME}}/patron_badges/2{{u.patron}}.webp?v=1" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.patron_tooltip}}" alt="{{u.patron_tooltip}}">
{% endif %}
{% if FEATURES['HOUSES'] and u.house %}
<img loading="lazy" class="ml-3" id="profile--house" src="/i/{{SITE_NAME}}/houses/{{u.house}}.webp?v=2000" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="House {{u.house}}" alt="House {{u.house}}">
{% endif %}
{% if u.verified %}
<span id="profile--verified"><i class="fas fa-badge-check align-middle ml-2 {% if u.verified=='Glowiefied' %}glow{% endif %}" style="color:{% if u.verifiedcolor %}#{{u.verifiedcolor}}{% else %}#1DA1F2{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.verified}}"></i></span>
{% endif %}
{% if u.admin_level >= PERMS['ADMIN_MOP_VISIBLE'] %}
<span id="profile--mop">
<i class="fas fa-broom text-admin align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Admin"></i>
</span>
{% endif %}
{% if v and v.id != u.id and v.has_follower(u) %}
<span class="followsyou badge badge-secondary text-small align-middle ml-2" id="profile--follows-you">Follows you</span>
{% endif %}
</div>
{% if FEATURES['PRONOUNS'] %}
<p class="font-weight-bolder" id="profile--pronouns" style="color: #{{u.titlecolor}}">{{u.pronouns}}</p>
{% endif %}
{% if u.customtitle %}
<p class="font-weight-bolder" id="profile--flair" style="color: #{{u.titlecolor}}">{{u.customtitle | safe}}</p>
{% endif %}
{% if v and (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']) -%}
<div class="font-weight-bolder mb-2" id="profile--simphate">
<a class="mr-1" href="/@{{u.username}}/views">Profile Views</a> | <a class="mx-1" href="/@{{u.username}}/upvoters">Simps</a> | <a class="mx-1" href="/@{{u.username}}/downvoters">Haters</a> | <a class="mx-1" href="/@{{u.username}}/upvoting">Simps For</a> | <a class="mx-1" href="/@{{u.username}}/downvoting">Hates</a> | <a class="ml-1" href="/@{{u.username}}/voted/posts">Voted</a>
</div>
{%- endif %}
<div class="font-weight-bolder">
<span id="profile-coins-amount">{{u.coins}}</span>
<img alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="20" src="{{'coins.webp' | asset_siteimg}}">
{% if FEATURES['MARSEYBUX'] %}
<span id="profile-bux-amount">{{u.marseybux}}</span>
<img alt="marseybux" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Marseybux" height="20" width="46" src="/i/marseybux.webp?v=2000">
{% endif %}
{% if PERMS['USER_FOLLOWS_VISIBLE'] == 0 or (v and v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']) -%}
<a class="mr-2" href="/@{{u.username}}/followers" id="profile--followers">{{u.stored_subscriber_count}} follower{{'s' if u.stored_subscriber_count != 1 else ''}}</a>
<a class="mr-2" href="/@{{u.username}}/following" id="profile--following">follows {{u.follow_count}} user{{'s' if u.follow_count != 1 else ''}}</a>
{%- endif %}
<span id="profile--joined">joined <span id="profile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile--joined--time','{{u.created_utc}}')">{{u.created_date}}</span></span>
{% if v and v.admin_level >= PERMS['VIEW_LAST_ACTIVE'] -%}
<span id="profile--lastactive" class="ml-2">last active <span id="profile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile--lastactive--time','{{u.last_active}}')">{{u.last_active_date}}</span></span>
{%- endif %}
</div>
{% if u.basedcount %}<p class="text-muted" id="profile--based">Based Count: {{u.basedcount}}</p>{% endif %}
{% if FEATURES['USERS_PROFILE_BODYTEXT'] -%}
{% if u.bio_html %}
<div class="text-muted font-weight-bolder mt-1" id="profile--bio">{{u.bio_html | safe}}</div>
{% else %}
<p class="text-muted" id="profile--bio">No bio...</p>
{% endif %}
{% if u.friends_html %}
<p class="text-muted font-weight-bold">Friends:</p>
<div id="profile--friends">{{u.friends_html | safe}}</div>
{% endif %}
{% if u.enemies_html %}
<p class="text-muted font-weight-bold">Enemies:</p>
<div id="profile--enemies">{{u.enemies_html | safe}}</div>
{% endif %}
{%- endif %}
{% if u.received_awards and FEATURES['AWARDS'] %}
<div class="text-white rounded p-2 mb-3" id="profile--awards" style="background-color: rgba(50, 50, 50, 0.6); width: 30%;">
<p class="text-uppercase my-0" style="font-weight: bold; font-size: 12px;">Awards received</p>
{% for a in u.received_awards %}
<span class="d-inline-block mx-1 profile--awards--award">
<i class="{{a['icon']}} {{a['color']}} fa-fw" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{a['title']}} Awards received"></i>
x{{a['count']}}
</span>
{% endfor %}
</div>
{% endif %}
{% if u.moderated_subs %}
<div class="text-white rounded p-2 mb-3" id="profile--holes" style="background-color: rgba(50, 50, 50, 0.6); width: 30%;">
<p class="text-uppercase my-0 pb-1" style="font-weight: bold; font-size: 12px;">Moderator of</p>
{% for a in u.moderated_subs %}
<span class="d-inline-block mx-1">
<a href="/h/{{a}}">/h/{{a}}</a>
</span>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div>
{% if v and v.id != u.id %}
<div id="profile--actionbtns">
<div class="actionbtns mb-3">
<button type="button" id="button-unsub" class="btn btn-secondary {% if not is_following %}d-none{% endif %}" onclick="postToastSwitch(this,'/unfollow/{{u.username}}','button-unsub','button-sub','d-none')">Unfollow</button>
<button type="button" id="button-sub" class="btn btn-primary {% if is_following or u.is_blocked %}d-none{% endif %}" onclick="postToastSwitch(this,'/follow/{{u.username}}','button-unsub','button-sub','d-none')">Follow</button>
<button type="button" class="btn btn-primary" onclick="toggleElement('message', 'input-message')">Message</button>
{% if FEATURES['USERS_SUICIDE'] -%}
<button type="button" class="btn btn-primary" onclick="postToastSwitch(this,'/@{{u.username}}/suicide')">Get Them Help</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="toggleElement('coin-transfer', 'coin-transfer-amount')">Gift Coins</button>
{% if FEATURES['MARSEYBUX'] -%}
<button type="button" class="btn btn-primary" onclick="toggleElement('bux-transfer', 'bux-transfer-amount')">Gift Marseybux</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="postToastReload(this,'/settings/block?username={{u.username}}')">Block</button>
</div>
</div>
<form class="d-none toggleable" id="message" action="/@{{u.username}}/message" onsubmit="sendMessage(event)">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" id="input-message" form="message" name="message" rows="3" minlength="1" maxlength="10000" class="form-control b2 mt-1" data-preview="message-preview" oninput="markdown(this)" required></textarea>
<pre class="btn btn-secondary format d-inline-block m-0 fas fa-smile-beam" onclick="loadEmojis('input-message')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></pre>
&nbsp;
<input type="submit" onclick="disable(this);remove_dialog()" value="Submit" class="btn btn-primary">
</form>
<div id="message-preview" class="preview mt-2"></div>
<div class="d-none mt-3 toggleable" id="coin-transfer">
<input autocomplete="off" id="coin-transfer-amount" class="form-control" name="amount" type="number" oninput="updateTax()">
<input autocomplete="off" id="coin-transfer-reason" maxlength=200 type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="coins-transfer-taxed">0</span> coins</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferCoins(this)">Gift</button>
</div>
<div class="d-none mt-3 toggleable" id="bux-transfer">
<input autocomplete="off" id="bux-transfer-amount" class="form-control" name="amount" type="number" oninput="updateBux()">
<input autocomplete="off" id="bux-transfer-reason" type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="bux-transfer-taxed">0</span> marseybux</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferBux(this)">Gift</button>
</div>
{{userpage_admintools.userAdminTools('desktop')}}
{% endif %}
<div class="actionbtns">
{% if v and v.id == u.id %}
<a href="/settings/personal" class="btn btn-secondary">Edit Profile</a>
<form id="upload-profile-background" action="/settings/profile_background" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<label class="format btn btn-primary" for="upload-profile-background-file" style="margin-bottom:0!important">
<i class="fas fa-image mr-1"></i>
{% if v.profile_background -%}
{{v.profile_background}}
{% else -%}
Upload Profile Background
{% endif %}
</label>
<input autocomplete="off" id="upload-profile-background-file" accept="image/*", type="file" name="file" onchange="this.form.submit()" hidden>
</form>
{% if v.profile_background -%}
<div class="d-flex mb-3">
<button type="button" class="btn btn-danger" onclick="postToastReload(this,'/settings/profile_background', 'DELETE')">
<i class="fas fa-image-slash mr-1"></i>
Remove current background
</button>
</div>
{%- endif %}
{% endif %}
{% if FEATURES['USERS_PROFILE_SONG'] and u.song and v and (v.id == u.id or v.mute and not u.unmutable) %}
<button type="button" class="btn btn-secondary" onclick="toggle()" {% if v.id == u.id %}style="margin-bottom:0!important;padding:0.3rem 0.75rem!important"{% endif %}>Toggle Anthem</button>
{% endif %}
</div>
<div class="mt-3" id="profile--info">
<p id="profile--info--id">User ID: {{u.id}}</p>
<p id="profile--info--spent">Coins spent: {{u.coins_spent}}</p>
<p id="profile--info--truescore">True score: {{u.truescore}}</p>
<p id="profile--info--winnings">Winnings: {{u.winnings}}</p>
<p id="profile--info--hats-owned" {% if u.num_of_owned_hats >= hats_total %}class="profile-owned-all-hats"{% endif %}>{{u.num_of_owned_hats}} / {{hats_total}} hats owned ({{hats_owned_percent}})</p>
{% if u.is_private %}
<p id="profile--info--private">User has private mode enabled</p>
{% endif %}
{% if v and (v.admin_level >= PERMS['VIEW_ALTS'] or v.alt) %}
{% if v.admin_level >= PERMS['USER_LINK'] %}
<span id="profile--alts"><a href="/@{{u.username}}/alts">Alts</a>:</span>
{% else %}
<span id="profile--alts">Alts:</span>
{% endif %}
<ul id="profile--alts-list">
{% for account in u.alts_unique if not account._alt_deleted and (v.can_see_shadowbanned or not account.shadowbanned) %}
<li><a href="{{account.url}}">@{{account.username}}</a>{% if account._is_manual %} [m]{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
{% if FEATURES['BADGES'] -%}
<div id="profile--badges">
{% for b in u.badges %}
{% if b.url %}
<a class="contain" rel="nofollow noopener" href="{{b.url}}">
<img alt="{{b.name}}" width=55 height=60 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
</a>
{% else %}
<img class="contain" alt="{{b.name}}" width=55 height=60 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
{% endif %}
{% endfor %}
</div>
{%- endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block mobileUserBanner %}
<div class="container-fluid pb-0 text-center bg-white d-md-none" style="margin-top:-6px;border-radius:0!important;">
<div class="row">
<div class="col px-0">
<a rel="nofollow noopener" href="{{u.banner_url}}">
<img alt="@{{u.username}}'s banner" onclick="expandDesktopImage()" loading="lazy" src="{{u.banner_url}}" width=100% style="object-fit:cover;max-height:30vh!important">
</a>
</div>
</div>
<div class="row border-bottom">
<div class="col">
<div style="margin-top: -34px;" id="profile-mobile--pfp">
<a rel="nofollow noopener" href="{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}" class="profile-pic-65-wrapper">
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" loading="lazy" src="{{u.profile_url}}" class="profile-pic-65 bg-white mb-2">
{% if u.hat_active -%}
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" class="profile-pic-65-hat hat" loading="lazy" src="{{u.hat_active}}?h=7">
{%- endif %}
</a>
</div>
<div class="mt-n3 py-3">
{{userpage_admintools.userBanBlock('mobile')}}
<h5 class=" d-inline-block" id="profile-mobile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.user_name}}</span></h5>
{% if u.username != u.original_username %}
<span id="profile-mobile--origname">
<i class="fas fa-user-tag text-info align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Original Username: @{{u.original_username}}"></i>
</span>
{% endif %}
{% if FEATURES['PATRON_ICONS'] and u.patron %}
<img loading="lazy" class="ml-2" src="/i/{{SITE_NAME}}/patron_badges/2{{u.patron}}.webp?v=1" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.patron_tooltip}}" alt="{{u.patron_tooltip}}">
{% endif %}
{% if FEATURES['HOUSES'] and u.house %}
<img loading="lazy" class="ml-2" id="profile-mobile--house" src="/i/{{SITE_NAME}}/houses/{{u.house}}.webp?v=2000" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="House {{u.house}}" alt="House {{u.house}}">
{% endif %}
{% if u.verified %}
<span id="profile-mobile--verified"><i class="fas fa-badge-check align-middle ml-2 {% if u.verified=='Glowiefied' %}glow{% endif %}" style="color:{% if u.verifiedcolor %}#{{u.verifiedcolor}}{% else %}#1DA1F2{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.verified}}"></i></span>&nbsp;
{% endif %}
{% if u.admin_level >= PERMS['ADMIN_MOP_VISIBLE'] %}
<span id="profile-mobile--mop">
<i class="fas fa-broom text-admin align-middle ml-1" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Admin"></i>
</span>
{% endif %}
{% if v and v.id != u.id and v.has_follower(u) %}
<span class="followsyou badge badge-secondary text-small align-middle mx-1" id="profile-mobile--follows-you">Follows you</span>
{% endif %}
{% if FEATURES['PRONOUNS'] %}
<p style="color: #{{u.titlecolor}}" id="profile-mobile--pronouns">{{u.pronouns}}</p>
{% endif %}
{% if u.customtitle %}
<p style="color: #{{u.titlecolor}}" id="profile-mobile--flair">{{u.customtitle | safe}}</p>
{% endif %}
{% if v and (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']) -%}
<div class="font-weight-bolder mb-2" id="profile-mobile--simphate">
<a class="mr-1" href="/@{{u.username}}/views">Profile Views</a> | <a class="mx-1" href="/@{{u.username}}/upvoters">Simps</a> | <a class="mx-1" href="/@{{u.username}}/downvoters">Haters</a> | <a class="mx-1" href="/@{{u.username}}/upvoting">Simps For</a> | <a class="mx-1" href="/@{{u.username}}/downvoting">Hates</a> | <a class="ml-1" href="/@{{u.username}}/voted/posts">Voted</a>
</div>
{%- endif %}
<div class="font-weight-normal">
<span id="profile-coins-amount-mobile" class="font-weight-bold">{{u.coins}}</span>
<img alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="15" src="{{'coins.webp' | asset_siteimg}}">
{% if FEATURES['MARSEYBUX'] %}
<span id="profile-bux-amount-mobile" class="font-weight-bold">{{u.marseybux}}</span>
<img alt="marseybux" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Marseybux" height="15" width="35" src="/i/marseybux.webp?v=2000">
{% endif %}
{% if PERMS['USER_FOLLOWS_VISIBLE'] == 0 or (v and v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']) -%}
<a href="/@{{u.username}}/followers" class="font-weight-bold mr-2" id="profile-mobile--followers">{{u.stored_subscriber_count}} follower{{'s' if u.stored_subscriber_count != 1 else ''}}</a>
<a href="/@{{u.username}}/following" class="font-weight-bold mr-2" id="profile-mobile--following" style="display:block">follows {{u.follow_count}} user{{'s' if u.follow_count != 1 else ''}}</a>
{%- endif %}
{% if u.basedcount %}
<br><span id="profile-mobile--based">Based count: {{u.basedcount}}</span>
{% endif %}
<br><span id="profile-mobile--joined">joined <span id="profile-mobile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--joined--time','{{u.created_utc}}')" class="font-weight-bold">{{u.created_date}}</span></span>
{% if v and v.admin_level >= PERMS['VIEW_LAST_ACTIVE'] -%}
<br><span id="profile-mobile--lastactive">last active <span id="profile-mobile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--lastactive--time','{{u.last_active}}')" class="font-weight-bold">{{u.last_active_date}}</span></span>
{%- endif %}
</div>
{% if FEATURES['USERS_PROFILE_BODYTEXT'] -%}
{% if u.bio_html %}
<div class="text-muted text-break" id="profile-mobile--bio">{{u.bio_html | safe}}</div>
{% endif %}
{% if u.friends_html %}
<p class="text-muted font-weight-bold mt-3">Friends:</p>
<div id="profile-mobile--friends">{{u.friends_html | safe}}</div>
{% endif %}
{% if u.enemies_html %}
<p class="text-muted font-weight-bold mt-3">Enemies:</p>
<div id="profile-mobile--enemies">{{u.enemies_html | safe}}</div>
{% endif %}
{%- endif %}
{% if u.received_awards and FEATURES['AWARDS'] %}
<div class="text-white rounded p-2 my-3 text-center" id="profile-mobile--awards" style="background-color: rgba(50, 50, 50, 0.6);">
<p class="text-uppercase my-0" style="font-weight: bold; font-size: 12px;">Awards received</p>
{% for a in u.received_awards %}
<span class="d-inline-block mx-1 profile-mobile--awards--award">
<i class="{{a['icon']}} {{a['color']}} fa-fw" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{a['title']}} Awards received"></i>
x{{a['count']}}
</span>
{% endfor %}
</div>
{% endif %}
{% if u.moderated_subs %}
<div class="text-white rounded p-2 mb-3" id="profile-mobile--holes" style="background-color: rgba(50, 50, 50, 0.6);">
<p class="text-uppercase my-0 pb-1" style="font-weight: bold; font-size: 12px;">Moderator of</p>
{% for a in u.moderated_subs %}
<span class="d-inline-block mx-1">
<a href="/h/{{a}}">/h/{{a}}</a>
</span>
{% endfor %}
</div>
{% endif %}
<div class="mb-3" id="profile-mobile--badges">
{% for b in u.badges %}
{% if b.url %}
<a rel="nofollow noopener" href="{{b.url}}">
<img class="contain" alt="{{b.name}}" width=29.33 height=32 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
</a>
{% else %}
<img class="contain" alt="{{b.name}}" width=29.33 height=32 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
{% endif %}
{% endfor %}
</div>
<div class="actionbtns">
{% if v and v.id == u.id %}
<a href="/settings/personal" class="btn btn-secondary ">Edit Profile</a>
{% endif %}
{% if FEATURES['USERS_PROFILE_SONG'] and u.song and v and (v.id == u.id or v.mute and not u.unmutable) %}
<button type="button" class="btn btn-secondary" onclick="toggle()" {% if v.id == u.id %}style="margin-bottom:0!important;padding:0.3rem 0.75rem!important"{% endif %}>Toggle Anthem</button>
{% endif %}
{% if v and v.id == u.id and (v.profile_background or v.theme == 'transparent') %}
<div>
<form class="mt-3" id="upload-profile-background-mobile" action="/settings/profile_background" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<label class="format btn btn-primary" for="upload-profile-background-file-mobile" style="margin-bottom:0!important">
<i class="fas fa-image mr-1"></i>
{% if v.profile_background %}
{{v.profile_background}}
{% else %}
Upload Profile Background
{% endif %}
</label>
<input autocomplete="off" id="upload-profile-background-file-mobile" accept="image/*", type="file" name="file" onchange="this.form.submit()" hidden>
</form>
{% if v.profile_background -%}
<div class="d-block mt-3 mb-3">
<button type="button" class="btn btn-danger" onclick="postToastReload(this,'/settings/profile_background', 'DELETE')">
<i class="fas fa-image-slash mr-1"></i>
Remove current background
</button>
</div>
{%- endif %}
</div>
{% endif %}
{% if v and v.id != u.id %}
<button type="button" id="button-unsub2" class="btn btn-secondary {% if not is_following %}d-none{% endif %}" onclick="postToastSwitch(this,'/unfollow/{{u.username}}','button-unsub2','button-sub2','d-none')">Unfollow</button>
<button type="button" id="button-sub2" class="btn btn-primary {% if is_following or u.is_blocked %}d-none{% endif %}" onclick="postToastSwitch(this,'/follow/{{u.username}}','button-unsub2','button-sub2','d-none')">Follow</button>
<button type="button" class="btn btn-primary" onclick="toggleElement('message-mobile', 'input-message-mobile')">Message</button>
{% if FEATURES['USERS_SUICIDE'] -%}
<button type="button" class="btn btn-primary" onclick="postToastSwitch(this,'/@{{u.username}}/suicide')">Get Them Help</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="toggleElement('coin-transfer-mobile', 'coin-transfer-amount-mobile')">Gift Coins</button>
{% if FEATURES['MARSEYBUX'] -%}
<button type="button" class="btn btn-primary" onclick="toggleElement('bux-transfer-mobile', 'bux-transfer-amount-mobile')">Gift Marseybux</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="postToastReload(this,'/settings/block?username={{u.username}}')">Block</button>
{% endif %}
</div>
{% if v and v.id != u.id %}
<form class="d-none toggleable" id='message-mobile' action="/@{{u.username}}/message" onsubmit="sendMessage(event)">
<input class="mt-1" type="hidden" name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" id="input-message-mobile" form="message-mobile" name="message" rows="3" minlength="1" maxlength="10000" class="form-control" data-preview="message-preview-mobile" oninput="markdown(this)" required></textarea>
<pre class="mt-1 btn btn-secondary format d-inline-block m-0 fas fa-smile-beam" onclick="loadEmojis('input-message-mobile')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></pre>
&nbsp;
<input type="submit" onclick="disable(this);remove_dialog()" value="Submit" class="btn btn-primary">
</form>
<div id="message-preview-mobile" class="preview my-3"></div>
<div class="d-none mt-3 toggleable" id="coin-transfer-mobile">
<input autocomplete="off" id="coin-transfer-amount-mobile" class="form-control" name="amount" type="number" oninput="updateTax(true)">
<input autocomplete="off" id="coin-transfer-reason-mobile" maxlength=200 type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="coins-transfer-taxed-mobile">0</span> coins</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferCoins(this, true)">Gift</button>
</div>
<div class="d-none mt-3 toggleable" id="bux-transfer-mobile">
<input autocomplete="off" id="bux-transfer-amount-mobile" class="form-control" name="amount" type="number" oninput="updateBux(true)">
<input autocomplete="off" id="bux-transfer-reason-mobile" type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="bux-transfer-taxed-mobile">0</span> marseybux</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferBux(this, true)">Gift</button>
</div>
{{userpage_admintools.userAdminTools('mobile')}}
{% endif %}
<div id="profile-mobile--info">
<p id="profile-mobile--info--id">User ID: {{u.id}}</p>
<p id="profile-mobile--info--spent">Coins spent: {{u.coins_spent}}</p>
<p id="profile-mobile--info--truescore">True score: {{u.truescore}}</p>
<p id="profile-mobile--info--winnings">Winnings: {{u.winnings}}</p>
<p id="profile-mobile--info--hats-owned" {% if u.num_of_owned_hats >= hats_total %}class="profile-owned-all-hats"{% endif %}>{{u.num_of_owned_hats}} / {{hats_total}} hats owned ({{hats_owned_percent}})</p>
{% if u.is_private %}
<p id="profile-mobile--info--private">User has private mode enabled</p>
{% endif %}
{% if v and (v.admin_level >= PERMS['VIEW_ALTS'] or v.alt) %}
{% if v.admin_level >= PERMS['USER_LINK'] %}
<span id="profile-mobile--alts"><a href="/@{{u.username}}/alts">Alts</a>:</span>
{% else %}
<span id="profile-mobile--alts">Alts:</span>
{% endif %}
<ul id="profile-mobile--alts-list">
{% for account in u.alts_unique if not account._alt_deleted and (v.can_see_shadowbanned or not account.shadowbanned) %}
<li><a href="{{account.url}}">@{{account.username}}</a>{% if account._is_manual %} [m]{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,86 +1,7 @@
{% extends "userpage/userpage.html" %} {% extends "userpage/userpage.html" %}
{% block userpage_content %}
{% block content %} <div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %} userpage-comments" style="margin-top: 10px;">
<div class="row no-gutters">
<div class="col"> <div class="col">
<div class="flex-row box-shadow-bottom d-flex justify-content-center justify-content-md-between align-items-center">
<ul class="nav settings-nav" id="profile-content--nav">
<li class="nav-item mr-1">
<a class="nav-link" href="/@{{u.username}}">Wall</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/posts">Posts <span class="count">({{u.post_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link {% if not 'saved' in request.path %}active{% endif %}" href="/@{{u.username}}/comments">Comments <span class="count">({{u.comment_count}})</span></a>
</li>
{% if u.id == v.id %}
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/saved/posts">Saved Posts <span class="count">({{u.saved_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link {% if 'saved' in request.path %}active{% endif %}" href="/@{{u.username}}/saved/comments">Saved Comments <span class="count">({{u.saved_comment_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/subscribed/posts">Subscribed <span class="count">({{u.subscribed_count}})</span></a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
{% if "/saved/" not in request.path and "/subscribed/" not in request.path %}
<div class="d-flex justify-content-between align-items-center pt-4">
<div class="d-flex align-items-center">
<div class="dropdown dropdown-actions">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if t=="hour" %}<i class="fas fa-clock mr-1"></i>
{% elif t=="day" %}<i class="fas fa-calendar-day mr-1"></i>
{% elif t=="week" %}<i class="fas fa-calendar-week mr-1"></i>
{% elif t=="month" %}<i class="fas fa-calendar-alt mr-1"></i>
{% elif t=="year" %}<i class="fas fa-calendar mr-1"></i>
{% elif t=="all" %}<i class="fas fa-infinity mr-1"></i>
{% endif %}
{{t | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if t != "hour" %}<a class="dropdown-item" href="?sort={{sort}}&t=hour"><i class="fas fa-clock mr-2"></i>Hour</a>{% endif %}
{% if t != "day" %}<a class="dropdown-item" href="?sort={{sort}}&t=day"><i class="fas fa-calendar-day mr-2"></i>Day</a>{% endif %}
{% if t != "week" %}<a class="dropdown-item" href="?sort={{sort}}&t=week"><i class="fas fa-calendar-week mr-2"></i>Week</a>{% endif %}
{% if t != "month" %}<a class="dropdown-item" href="?sort={{sort}}&t=month"><i class="fas fa-calendar-alt mr-2"></i>Month</a>{% endif %}
{% if t != "year" %}<a class="dropdown-item" href="?sort={{sort}}&t=year"><i class="fas fa-calendar mr-2"></i>Year</a>{% endif %}
{% if t != "all" %}<a class="dropdown-item" href="?sort={{sort}}&t=all"><i class="fas fa-infinity mr-2"></i>All</a>{% endif %}
</div>
</div>
<div class="dropdown dropdown-actions ml-3">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton2" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if sort=="top" %}<i class="fas fa-arrow-alt-circle-up mr-1"></i>{% endif %}
{% if sort=="bottom" %}<i class="fas fa-arrow-alt-circle-down mr-1"></i>{% endif %}
{% if sort=="new" %}<i class="fas fa-sparkles mr-1"></i>{% endif %}
{% if sort=="old" %}<i class="fas fa-book mr-1"></i>{% endif %}
{% if sort=="controversial" %}<i class="fas fa-bullhorn mr-1"></i>{% endif %}
{{sort | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton2" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if sort != "top" %}<a class="dropdown-item" href="?sort=top&t={{t}}"><i class="fas fa-arrow-alt-circle-up mr-2"></i>Top</a>{% endif %}
{% if sort != "bottom" %}<a class="dropdown-item" href="?sort=bottom&t={{t}}"><i class="fas fa-arrow-alt-circle-down mr-2"></i>Bottom</a>{% endif %}
{% if sort != "new" %}<a class="dropdown-item" href="?sort=new&t={{t}}"><i class="fas fa-sparkles mr-2"></i>New</a>{% endif %}
{% if sort != "old" %}<a class="dropdown-item" href="?sort=old&t={{t}}"><i class="fas fa-book mr-2"></i>Old</a>{% endif %}
{% if sort != "controversial" %}<a class="dropdown-item" href="?sort=controversial&t={{t}}"><i class="fas fa-bullhorn mr-2"></i>Controversial</a>{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %}" style="margin-top: 10px;">
<div class="col">
{% if listing %} {% if listing %}
<div class="posts p-3 p-md-0"> <div class="posts p-3 p-md-0">
{% with comments=listing %} {% with comments=listing %}
@ -107,20 +28,4 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if u.song %}
{% if v and v.id == u.id %}
<div id="v_username" class="d-none">{{v.username}}</div>
{% else %}
<div id="u_username" class="d-none">{{u.username}}</div>
{% endif %}
{% endif %}
{% if v %}
<div id='tax' class="d-none">{% if v.patron or u.patron %}0{% else %}0.03{% endif %}</div>
<div id="username" class="d-none">{{u.username}}</div>
<script defer src="{{'js/userpage_v.js' | asset}}"></script>
{% endif %}
<script defer src="{{'js/userpage.js' | asset}}"></script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,74 @@
{% set path = request.path.replace("/@" + u.username, '') %}
<div class="row no-gutters">
<div class="col">
<div class="flex-row box-shadow-bottom d-flex justify-content-center justify-content-md-between align-items-center">
<ul class="nav settings-nav" id="profile-content--nav">
<li class="nav-item mr-1">
<a class="nav-link{% if path == '' %} active{% endif %}" href="/@{{u.username}}">Wall</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == '/posts' %} active{% endif %}" href="/@{{u.username}}/posts">Posts <span class="count">({{u.post_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == '/comments' %} active{% endif %}" href="/@{{u.username}}/comments">Comments <span class="count">({{u.comment_count}})</span></a>
</li>
{% if u.id == v.id %}
<li class="nav-item">
<a class="nav-link{% if path == '/saved/posts' %} active{% endif %}" href="/@{{u.username}}/saved/posts">Saved Posts <span class="count">({{u.saved_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == '/saved/comments' %} active{% endif %}" href="/@{{u.username}}/saved/comments">Saved Comments <span class="count">({{u.saved_comment_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == '/subscribed/posts' %} active{% endif %}" href="/@{{u.username}}/subscribed/posts">Subscribed <span class="count">({{u.subscribed_count}})</span></a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
{% if "/saved/" not in request.path and "/subscribed/" not in request.path and not path == '' %}
<div class="d-flex justify-content-between align-items-center" style="padding-top:10px">
<div class="d-flex align-items-center">
<div class="dropdown dropdown-actions">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if t=="hour" %}<i class="fas fa-clock mr-1"></i>
{% elif t=="day" %}<i class="fas fa-calendar-day mr-1"></i>
{% elif t=="week" %}<i class="fas fa-calendar-week mr-1"></i>
{% elif t=="month" %}<i class="fas fa-calendar-alt mr-1"></i>
{% elif t=="year" %}<i class="fas fa-calendar mr-1"></i>
{% elif t=="all" %}<i class="fas fa-infinity mr-1"></i>
{% endif %}
{{t | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if t != "hour" %}<a class="dropdown-item" href="?sort={{sort}}&t=hour"><i class="fas fa-clock mr-2"></i>Hour</a>{% endif %}
{% if t != "day" %}<a class="dropdown-item" href="?sort={{sort}}&t=day"><i class="fas fa-calendar-day mr-2"></i>Day</a>{% endif %}
{% if t != "week" %}<a class="dropdown-item" href="?sort={{sort}}&t=week"><i class="fas fa-calendar-week mr-2"></i>Week</a>{% endif %}
{% if t != "month" %}<a class="dropdown-item" href="?sort={{sort}}&t=month"><i class="fas fa-calendar-alt mr-2"></i>Month</a>{% endif %}
{% if t != "year" %}<a class="dropdown-item" href="?sort={{sort}}&t=year"><i class="fas fa-calendar mr-2"></i>Year</a>{% endif %}
{% if t != "all" %}<a class="dropdown-item" href="?sort={{sort}}&t=all"><i class="fas fa-infinity mr-2"></i>All</a>{% endif %}
</div>
</div>
<div class="dropdown dropdown-actions ml-3">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton2" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if sort=="top" %}<i class="fas fa-arrow-alt-circle-up mr-1"></i>{% endif %}
{% if sort=="bottom" %}<i class="fas fa-arrow-alt-circle-down mr-1"></i>{% endif %}
{% if sort=="new" %}<i class="fas fa-sparkles mr-1"></i>{% endif %}
{% if sort=="old" %}<i class="fas fa-book mr-1"></i>{% endif %}
{% if sort=="controversial" %}<i class="fas fa-bullhorn mr-1"></i>{% endif %}
{{sort | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton2" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if sort != "top" %}<a class="dropdown-item" href="?sort=top&t={{t}}"><i class="fas fa-arrow-alt-circle-up mr-2"></i>Top</a>{% endif %}
{% if sort != "bottom" %}<a class="dropdown-item" href="?sort=bottom&t={{t}}"><i class="fas fa-arrow-alt-circle-down mr-2"></i>Bottom</a>{% endif %}
{% if sort != "new" %}<a class="dropdown-item" href="?sort=new&t={{t}}"><i class="fas fa-sparkles mr-2"></i>New</a>{% endif %}
{% if sort != "old" %}<a class="dropdown-item" href="?sort=old&t={{t}}"><i class="fas fa-book mr-2"></i>Old</a>{% endif %}
{% if sort != "controversial" %}<a class="dropdown-item" href="?sort=controversial&t={{t}}"><i class="fas fa-bullhorn mr-2"></i>Controversial</a>{% endif %}
</div>
</div>
</div>
</div>
{% endif %}

View File

@ -1,8 +1,6 @@
{% extends "userpage/userpage.html" %} {% extends "userpage/userpage.html" %}
{% block userpage_content %}
<div class="row justify-content-center userpage-private">
{% block content %}
<div class="row justify-content-center">
<div class="col-10 col-md-5"> <div class="col-10 col-md-5">
<div class="text-center px-3 my-3"> <div class="text-center px-3 my-3">
<span class="fa-stack fa-2x text-muted mb-4"> <span class="fa-stack fa-2x text-muted mb-4">
@ -14,29 +12,4 @@
</div> </div>
</div> </div>
</div> </div>
{% if u.song %}
{% if v and v.id == u.id %}
<div id="v_username" class="d-none">{{v.username}}</div>
{% else %}
<div id="u_username" class="d-none">{{u.username}}</div>
{% endif %}
{% endif %}
{% endblock %}
{% block pagenav %}
{% if u.song %}
<div id="uid" class="d-none">{{u.id}}</div>
{% endif %}
{% if v %}
<div id='tax' class="d-none">{% if v.patron or u.patron %}0{% else %}0.03{% endif %}</div>
<div id="username" class="d-none">{{u.username}}</div>
{%- include "modals/emoji.html" -%}
<script defer src="{{'js/userpage_v.js' | asset}}"></script>
{% endif %}
<script defer src="{{'js/userpage.js' | asset}}"></script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,10 @@
{%- extends 'userpage/userpage.html' -%}
{% block userpage_content %}
<div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %} userpage-submissions" style="margin-top: 10px;">
<div class="col">
<div class="posts">
{% include "submission_listing.html" %}
</div>
</div>
</div>
{% endblock %}

View File

@ -14,606 +14,10 @@
<link rel="stylesheet" href="/{{u.id}}/profilecss"> <link rel="stylesheet" href="/{{u.id}}/profilecss">
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% import 'userpage/admintools.html' as userpage_admintools with context %}
{% set hats_total = u.hats_owned_proportion_display[1] if u else 0 %}
{% set hats_owned_percent = u.hats_owned_proportion_display[0] if u else '' %}
{% block desktopUserBanner %}
<div class="row d-mob-none">
<div class="col px-0">
<div class="jumbotron jumbotron-fluid jumbotron-guild d-mob-none" {% if FEATURES['USERS_PROFILE_BANNER'] %}style="background-image: url({{u.banner_url}})"{% endif %}>
<div class="jumbotron-overlay"></div>
<div class="w-100 my-3">
<div class="container-fluid nobackground">
<div class="d-md-flex text-center text-md-left">
<div id="profile--pfp" {% if u.hat_active %}class="profile--pfp--hat hat"{% endif %}>
<a rel="nofollow noopener" href="{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}" class="profile-pic-100-wrapper">
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" loading="lazy" src="{{u.profile_url}}" class="profile-pic profile-pic-100 mb-5">
{% if u.hat_active -%}
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" class="profile-pic-100-hat hat" loading="lazy" src="{{u.hat_active}}?h=7" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.hat_tooltip(v)}}">
{%- endif %}
</a>
</div>
<div id="profilestuff" class="ml-3 w-100">
{{userpage_admintools.userBanBlock('desktop')}}
<div class="d-flex align-items-center mt-1 mb-2">
<h3 class="font-weight-bolder my-0 mr-2" id="profile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.user_name}}</span></h3>
{% if u.username != u.original_username %}
<span id="profile--origname">
<i class="fas fa-user-tag text-info align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Original Username: @{{u.original_username}}"></i>
</span>
{% endif %}
{% if FEATURES['PATRON_ICONS'] and u.patron %}
<img loading="lazy" class="ml-3" src="/i/{{SITE_NAME}}/patron_badges/2{{u.patron}}.webp?v=1" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.patron_tooltip}}" alt="{{u.patron_tooltip}}">
{% endif %}
{% if FEATURES['HOUSES'] and u.house %}
<img loading="lazy" class="ml-3" id="profile--house" src="/i/{{SITE_NAME}}/houses/{{u.house}}.webp?v=2000" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="House {{u.house}}" alt="House {{u.house}}">
{% endif %}
{% if u.verified %}
<span id="profile--verified"><i class="fas fa-badge-check align-middle ml-2 {% if u.verified=='Glowiefied' %}glow{% endif %}" style="color:{% if u.verifiedcolor %}#{{u.verifiedcolor}}{% else %}#1DA1F2{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.verified}}"></i></span>
{% endif %}
{% if u.admin_level >= PERMS['ADMIN_MOP_VISIBLE'] %}
<span id="profile--mop">
<i class="fas fa-broom text-admin align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Admin"></i>
</span>
{% endif %}
{% if v and v.id != u.id and v.has_follower(u) %}
<span class="followsyou badge badge-secondary text-small align-middle ml-2" id="profile--follows-you">Follows you</span>
{% endif %}
</div>
{% if FEATURES['PRONOUNS'] %}
<p class="font-weight-bolder" id="profile--pronouns" style="color: #{{u.titlecolor}}">{{u.pronouns}}</p>
{% endif %}
{% if u.customtitle %}
<p class="font-weight-bolder" id="profile--flair" style="color: #{{u.titlecolor}}">{{u.customtitle | safe}}</p>
{% endif %}
{% if v and (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']) -%}
<div class="font-weight-bolder mb-2" id="profile--simphate">
<a class="mr-1" href="/@{{u.username}}/views">Profile Views</a> | <a class="mx-1" href="/@{{u.username}}/upvoters">Simps</a> | <a class="mx-1" href="/@{{u.username}}/downvoters">Haters</a> | <a class="mx-1" href="/@{{u.username}}/upvoting">Simps For</a> | <a class="mx-1" href="/@{{u.username}}/downvoting">Hates</a> | <a class="ml-1" href="/@{{u.username}}/voted/posts">Voted</a>
</div>
{%- endif %}
<div class="font-weight-bolder">
<span id="profile-coins-amount">{{u.coins}}</span>
<img alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="20" src="{{'coins.webp' | asset_siteimg}}">
{% if FEATURES['MARSEYBUX'] %}
<span id="profile-bux-amount">{{u.marseybux}}</span>
<img alt="marseybux" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Marseybux" height="20" width="46" src="/i/marseybux.webp?v=2000">
{% endif %}
{% if PERMS['USER_FOLLOWS_VISIBLE'] == 0 or (v and v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']) -%}
<a class="mr-2" href="/@{{u.username}}/followers" id="profile--followers">{{u.stored_subscriber_count}} follower{{'s' if u.stored_subscriber_count != 1 else ''}}</a>
<a class="mr-2" href="/@{{u.username}}/following" id="profile--following">follows {{u.follow_count}} user{{'s' if u.follow_count != 1 else ''}}</a>
{%- endif %}
<span id="profile--joined">joined <span id="profile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile--joined--time','{{u.created_utc}}')">{{u.created_date}}</span></span>
{% if v and v.admin_level >= PERMS['VIEW_LAST_ACTIVE'] -%}
<span id="profile--lastactive" class="ml-2">last active <span id="profile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile--lastactive--time','{{u.last_active}}')">{{u.last_active_date}}</span></span>
{%- endif %}
</div>
{% if u.basedcount %}<p class="text-muted" id="profile--based">Based Count: {{u.basedcount}}</p>{% endif %}
{% if FEATURES['USERS_PROFILE_BODYTEXT'] -%}
{% if u.bio_html %}
<div class="text-muted font-weight-bolder mt-1" id="profile--bio">{{u.bio_html | safe}}</div>
{% else %}
<p class="text-muted" id="profile--bio">No bio...</p>
{% endif %}
{% if u.friends_html %}
<p class="text-muted font-weight-bold">Friends:</p>
<div id="profile--friends">{{u.friends_html | safe}}</div>
{% endif %}
{% if u.enemies_html %}
<p class="text-muted font-weight-bold">Enemies:</p>
<div id="profile--enemies">{{u.enemies_html | safe}}</div>
{% endif %}
{%- endif %}
{% if u.received_awards and FEATURES['AWARDS'] %}
<div class="text-white rounded p-2 mb-3" id="profile--awards" style="background-color: rgba(50, 50, 50, 0.6); width: 30%;">
<p class="text-uppercase my-0" style="font-weight: bold; font-size: 12px;">Awards received</p>
{% for a in u.received_awards %}
<span class="d-inline-block mx-1 profile--awards--award">
<i class="{{a['icon']}} {{a['color']}} fa-fw" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{a['title']}} Awards received"></i>
x{{a['count']}}
</span>
{% endfor %}
</div>
{% endif %}
{% if u.moderated_subs %}
<div class="text-white rounded p-2 mb-3" id="profile--holes" style="background-color: rgba(50, 50, 50, 0.6); width: 30%;">
<p class="text-uppercase my-0 pb-1" style="font-weight: bold; font-size: 12px;">Moderator of</p>
{% for a in u.moderated_subs %}
<span class="d-inline-block mx-1">
<a href="/h/{{a}}">/h/{{a}}</a>
</span>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-between align-items-center">
<div>
{% if v and v.id != u.id %}
<div id="profile--actionbtns">
<div class="actionbtns mb-3">
<button type="button" id="button-unsub" class="btn btn-secondary {% if not is_following %}d-none{% endif %}" onclick="postToastSwitch(this,'/unfollow/{{u.username}}','button-unsub','button-sub','d-none')">Unfollow</button>
<button type="button" id="button-sub" class="btn btn-primary {% if is_following or u.is_blocked %}d-none{% endif %}" onclick="postToastSwitch(this,'/follow/{{u.username}}','button-unsub','button-sub','d-none')">Follow</button>
<button type="button" class="btn btn-primary" onclick="toggleElement('message', 'input-message')">Message</button>
{% if FEATURES['USERS_SUICIDE'] -%}
<button type="button" class="btn btn-primary" onclick="postToastSwitch(this,'/@{{u.username}}/suicide')">Get Them Help</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="toggleElement('coin-transfer', 'coin-transfer-amount')">Gift Coins</button>
{% if FEATURES['MARSEYBUX'] -%}
<button type="button" class="btn btn-primary" onclick="toggleElement('bux-transfer', 'bux-transfer-amount')">Gift Marseybux</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="postToastReload(this,'/settings/block?username={{u.username}}')">Block</button>
</div>
</div>
<form class="d-none toggleable" id="message" action="/@{{u.username}}/message" onsubmit="sendMessage(event)">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" id="input-message" form="message" name="message" rows="3" minlength="1" maxlength="10000" class="form-control b2 mt-1" data-preview="message-preview" oninput="markdown(this)" required></textarea>
<pre class="btn btn-secondary format d-inline-block m-0 fas fa-smile-beam" onclick="loadEmojis('input-message')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></pre>
&nbsp;
<input type="submit" onclick="disable(this);remove_dialog()" value="Submit" class="btn btn-primary">
</form>
<div id="message-preview" class="preview mt-2"></div>
<div class="d-none mt-3 toggleable" id="coin-transfer">
<input autocomplete="off" id="coin-transfer-amount" class="form-control" name="amount" type="number" oninput="updateTax()">
<input autocomplete="off" id="coin-transfer-reason" maxlength=200 type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="coins-transfer-taxed">0</span> coins</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferCoins(this)">Gift</button>
</div>
<div class="d-none mt-3 toggleable" id="bux-transfer">
<input autocomplete="off" id="bux-transfer-amount" class="form-control" name="amount" type="number" oninput="updateBux()">
<input autocomplete="off" id="bux-transfer-reason" type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="bux-transfer-taxed">0</span> marseybux</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferBux(this)">Gift</button>
</div>
{{userpage_admintools.userAdminTools('desktop')}}
{% endif %}
<div class="actionbtns">
{% if v and v.id == u.id %}
<a href="/settings/personal" class="btn btn-secondary">Edit Profile</a>
<form id="upload-profile-background" action="/settings/profile_background" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<label class="format btn btn-primary" for="upload-profile-background-file" style="margin-bottom:0!important">
<i class="fas fa-image mr-1"></i>
{% if v.profile_background -%}
{{v.profile_background}}
{% else -%}
Upload Profile Background
{% endif %}
</label>
<input autocomplete="off" id="upload-profile-background-file" accept="image/*", type="file" name="file" onchange="this.form.submit()" hidden>
</form>
{% if v.profile_background -%}
<div class="d-flex mb-3">
<button type="button" class="btn btn-danger" onclick="postToastReload(this,'/settings/profile_background', 'DELETE')">
<i class="fas fa-image-slash mr-1"></i>
Remove current background
</button>
</div>
{%- endif %}
{% endif %}
{% if FEATURES['USERS_PROFILE_SONG'] and u.song and v and (v.id == u.id or v.mute and not u.unmutable) %}
<button type="button" class="btn btn-secondary" onclick="toggle()" {% if v.id == u.id %}style="margin-bottom:0!important;padding:0.3rem 0.75rem!important"{% endif %}>Toggle Anthem</button>
{% endif %}
</div>
<div class="mt-3" id="profile--info">
<p id="profile--info--id">User ID: {{u.id}}</p>
<p id="profile--info--spent">Coins spent: {{u.coins_spent}}</p>
<p id="profile--info--truescore">True score: {{u.truescore}}</p>
<p id="profile--info--winnings">Winnings: {{u.winnings}}</p>
<p id="profile--info--hats-owned" {% if u.num_of_owned_hats >= hats_total %}class="profile-owned-all-hats"{% endif %}>{{u.num_of_owned_hats}} / {{hats_total}} hats owned ({{hats_owned_percent}})</p>
{% if u.is_private %}
<p id="profile--info--private">User has private mode enabled</p>
{% endif %}
{% if v and (v.admin_level >= PERMS['VIEW_ALTS'] or v.alt) %}
{% if v.admin_level >= PERMS['USER_LINK'] %}
<span id="profile--alts"><a href="/@{{u.username}}/alts">Alts</a>:</span>
{% else %}
<span id="profile--alts">Alts:</span>
{% endif %}
<ul id="profile--alts-list">
{% for account in u.alts_unique if not account._alt_deleted and (v.can_see_shadowbanned or not account.shadowbanned) %}
<li><a href="{{account.url}}">@{{account.username}}</a>{% if account._is_manual %} [m]{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
{% if FEATURES['BADGES'] -%}
<div id="profile--badges">
{% for b in u.badges %}
{% if b.url %}
<a class="contain" rel="nofollow noopener" href="{{b.url}}">
<img alt="{{b.name}}" width=55 height=60 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
</a>
{% else %}
<img class="contain" alt="{{b.name}}" width=55 height=60 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
{% endif %}
{% endfor %}
</div>
{%- endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block mobileUserBanner %}
<div class="container-fluid pb-0 text-center bg-white d-md-none" style="margin-top:-6px;border-radius:0!important;">
<div class="row">
<div class="col px-0">
<a rel="nofollow noopener" href="{{u.banner_url}}">
<img alt="@{{u.username}}'s banner" onclick="expandDesktopImage()" loading="lazy" src="{{u.banner_url}}" width=100% style="object-fit:cover;max-height:30vh!important">
</a>
</div>
</div>
<div class="row border-bottom">
<div class="col">
<div style="margin-top: -34px;" id="profile-mobile--pfp">
<a rel="nofollow noopener" href="{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}" class="profile-pic-65-wrapper">
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" loading="lazy" src="{{u.profile_url}}" class="profile-pic-65 bg-white mb-2">
{% if u.hat_active -%}
<img onclick="expandDesktopImage('{% if u.highres %}{{u.highres}}{% else %}{{u.profile_url}}{% endif %}')" class="profile-pic-65-hat hat" loading="lazy" src="{{u.hat_active}}?h=7">
{%- endif %}
</a>
</div>
<div class="mt-n3 py-3">
{{userpage_admintools.userBanBlock('mobile')}}
<h5 class=" d-inline-block" id="profile-mobile--name" style="color: #{{u.name_color}}"><span {% if u.patron %}class="patron" style="background-color:#{{u.name_color}}"{% endif %}>{{u.user_name}}</span></h5>
{% if u.username != u.original_username %}
<span id="profile-mobile--origname">
<i class="fas fa-user-tag text-info align-middle ml-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Original Username: @{{u.original_username}}"></i>
</span>
{% endif %}
{% if FEATURES['PATRON_ICONS'] and u.patron %}
<img loading="lazy" class="ml-2" src="/i/{{SITE_NAME}}/patron_badges/2{{u.patron}}.webp?v=1" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.patron_tooltip}}" alt="{{u.patron_tooltip}}">
{% endif %}
{% if FEATURES['HOUSES'] and u.house %}
<img loading="lazy" class="ml-2" id="profile-mobile--house" src="/i/{{SITE_NAME}}/houses/{{u.house}}.webp?v=2000" height="20" data-bs-toggle="tooltip" data-bs-placement="bottom" title="House {{u.house}}" alt="House {{u.house}}">
{% endif %}
{% if u.verified %}
<span id="profile-mobile--verified"><i class="fas fa-badge-check align-middle ml-2 {% if u.verified=='Glowiefied' %}glow{% endif %}" style="color:{% if u.verifiedcolor %}#{{u.verifiedcolor}}{% else %}#1DA1F2{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{u.verified}}"></i></span>&nbsp;
{% endif %}
{% if u.admin_level >= PERMS['ADMIN_MOP_VISIBLE'] %}
<span id="profile-mobile--mop">
<i class="fas fa-broom text-admin align-middle ml-1" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Admin"></i>
</span>
{% endif %}
{% if v and v.id != u.id and v.has_follower(u) %}
<span class="followsyou badge badge-secondary text-small align-middle mx-1" id="profile-mobile--follows-you">Follows you</span>
{% endif %}
{% if FEATURES['PRONOUNS'] %}
<p style="color: #{{u.titlecolor}}" id="profile-mobile--pronouns">{{u.pronouns}}</p>
{% endif %}
{% if u.customtitle %}
<p style="color: #{{u.titlecolor}}" id="profile-mobile--flair">{{u.customtitle | safe}}</p>
{% endif %}
{% if v and (v.id == u.id or v.admin_level >= PERMS['USER_VOTERS_VISIBLE']) -%}
<div class="font-weight-bolder mb-2" id="profile-mobile--simphate">
<a class="mr-1" href="/@{{u.username}}/views">Profile Views</a> | <a class="mx-1" href="/@{{u.username}}/upvoters">Simps</a> | <a class="mx-1" href="/@{{u.username}}/downvoters">Haters</a> | <a class="mx-1" href="/@{{u.username}}/upvoting">Simps For</a> | <a class="mx-1" href="/@{{u.username}}/downvoting">Hates</a> | <a class="ml-1" href="/@{{u.username}}/voted/posts">Voted</a>
</div>
{%- endif %}
<div class="font-weight-normal">
<span id="profile-coins-amount-mobile" class="font-weight-bold">{{u.coins}}</span>
<img alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="15" src="{{'coins.webp' | asset_siteimg}}">
{% if FEATURES['MARSEYBUX'] %}
<span id="profile-bux-amount-mobile" class="font-weight-bold">{{u.marseybux}}</span>
<img alt="marseybux" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Marseybux" height="15" width="35" src="/i/marseybux.webp?v=2000">
{% endif %}
{% if PERMS['USER_FOLLOWS_VISIBLE'] == 0 or (v and v.admin_level >= PERMS['USER_FOLLOWS_VISIBLE']) -%}
<a href="/@{{u.username}}/followers" class="font-weight-bold mr-2" id="profile-mobile--followers">{{u.stored_subscriber_count}} follower{{'s' if u.stored_subscriber_count != 1 else ''}}</a>
<a href="/@{{u.username}}/following" class="font-weight-bold mr-2" id="profile-mobile--following" style="display:block">follows {{u.follow_count}} user{{'s' if u.follow_count != 1 else ''}}</a>
{%- endif %}
{% if u.basedcount %}
<br><span id="profile-mobile--based">Based count: {{u.basedcount}}</span>
{% endif %}
<br><span id="profile-mobile--joined">joined <span id="profile-mobile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--joined--time','{{u.created_utc}}')" class="font-weight-bold">{{u.created_date}}</span></span>
{% if v and v.admin_level >= PERMS['VIEW_LAST_ACTIVE'] -%}
<br><span id="profile-mobile--lastactive">last active <span id="profile-mobile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--lastactive--time','{{u.last_active}}')" class="font-weight-bold">{{u.last_active_date}}</span></span>
{%- endif %}
</div>
{% if FEATURES['USERS_PROFILE_BODYTEXT'] -%}
{% if u.bio_html %}
<div class="text-muted text-break" id="profile-mobile--bio">{{u.bio_html | safe}}</div>
{% endif %}
{% if u.friends_html %}
<p class="text-muted font-weight-bold mt-3">Friends:</p>
<div id="profile-mobile--friends">{{u.friends_html | safe}}</div>
{% endif %}
{% if u.enemies_html %}
<p class="text-muted font-weight-bold mt-3">Enemies:</p>
<div id="profile-mobile--enemies">{{u.enemies_html | safe}}</div>
{% endif %}
{%- endif %}
{% if u.received_awards and FEATURES['AWARDS'] %}
<div class="text-white rounded p-2 my-3 text-center" id="profile-mobile--awards" style="background-color: rgba(50, 50, 50, 0.6);">
<p class="text-uppercase my-0" style="font-weight: bold; font-size: 12px;">Awards received</p>
{% for a in u.received_awards %}
<span class="d-inline-block mx-1 profile-mobile--awards--award">
<i class="{{a['icon']}} {{a['color']}} fa-fw" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{a['title']}} Awards received"></i>
x{{a['count']}}
</span>
{% endfor %}
</div>
{% endif %}
{% if u.moderated_subs %}
<div class="text-white rounded p-2 mb-3" id="profile-mobile--holes" style="background-color: rgba(50, 50, 50, 0.6);">
<p class="text-uppercase my-0 pb-1" style="font-weight: bold; font-size: 12px;">Moderator of</p>
{% for a in u.moderated_subs %}
<span class="d-inline-block mx-1">
<a href="/h/{{a}}">/h/{{a}}</a>
</span>
{% endfor %}
</div>
{% endif %}
<div class="mb-3" id="profile-mobile--badges">
{% for b in u.badges %}
{% if b.url %}
<a rel="nofollow noopener" href="{{b.url}}">
<img class="contain" alt="{{b.name}}" width=29.33 height=32 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
</a>
{% else %}
<img class="contain" alt="{{b.name}}" width=29.33 height=32 loading="lazy" src="{{b.path}}?b=5" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{b.text}}" {% if b.until %}data-until="{{b.until}}" onmouseover="badge_timestamp(this)"{% endif %}>
{% endif %}
{% endfor %}
</div>
<div class="actionbtns">
{% if v and v.id == u.id %}
<a href="/settings/personal" class="btn btn-secondary ">Edit Profile</a>
{% endif %}
{% if FEATURES['USERS_PROFILE_SONG'] and u.song and v and (v.id == u.id or v.mute and not u.unmutable) %}
<button type="button" class="btn btn-secondary" onclick="toggle()" {% if v.id == u.id %}style="margin-bottom:0!important;padding:0.3rem 0.75rem!important"{% endif %}>Toggle Anthem</button>
{% endif %}
{% if v and v.id == u.id %}
<div>
<form class="mt-3" id="upload-profile-background-mobile" action="/settings/profile_background" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<label class="format btn btn-primary" for="upload-profile-background-file-mobile" style="margin-bottom:0!important">
<i class="fas fa-image mr-1"></i>
{% if v.profile_background %}
{{v.profile_background}}
{% else %}
Upload Profile Background
{% endif %}
</label>
<input autocomplete="off" id="upload-profile-background-file-mobile" accept="image/*", type="file" name="file" onchange="this.form.submit()" hidden>
</form>
{% if v.profile_background -%}
<div class="d-block mt-3 mb-3">
<button type="button" class="btn btn-danger" onclick="postToastReload(this,'/settings/profile_background', 'DELETE')">
<i class="fas fa-image-slash mr-1"></i>
Remove current background
</button>
</div>
{%- endif %}
</div>
{% endif %}
{% if v and v.id != u.id %}
<button type="button" id="button-unsub2" class="btn btn-secondary {% if not is_following %}d-none{% endif %}" onclick="postToastSwitch(this,'/unfollow/{{u.username}}','button-unsub2','button-sub2','d-none')">Unfollow</button>
<button type="button" id="button-sub2" class="btn btn-primary {% if is_following or u.is_blocked %}d-none{% endif %}" onclick="postToastSwitch(this,'/follow/{{u.username}}','button-unsub2','button-sub2','d-none')">Follow</button>
<button type="button" class="btn btn-primary" onclick="toggleElement('message-mobile', 'input-message-mobile')">Message</button>
{% if FEATURES['USERS_SUICIDE'] -%}
<button type="button" class="btn btn-primary" onclick="postToastSwitch(this,'/@{{u.username}}/suicide')">Get Them Help</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="toggleElement('coin-transfer-mobile', 'coin-transfer-amount-mobile')">Gift Coins</button>
{% if FEATURES['MARSEYBUX'] -%}
<button type="button" class="btn btn-primary" onclick="toggleElement('bux-transfer-mobile', 'bux-transfer-amount-mobile')">Gift Marseybux</button>
{%- endif %}
<button type="button" class="btn btn-primary" onclick="postToastReload(this,'/settings/block?username={{u.username}}')">Block</button>
{% endif %}
</div>
{% if v and v.id != u.id %}
<form class="d-none toggleable" id='message-mobile' action="/@{{u.username}}/message" onsubmit="sendMessage(event)">
<input class="mt-1" type="hidden" name="formkey" value="{{v|formkey}}">
<textarea autocomplete="off" id="input-message-mobile" form="message-mobile" name="message" rows="3" minlength="1" maxlength="10000" class="form-control" data-preview="message-preview-mobile" oninput="markdown(this)" required></textarea>
<pre class="mt-1 btn btn-secondary format d-inline-block m-0 fas fa-smile-beam" onclick="loadEmojis('input-message-mobile')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></pre>
&nbsp;
<input type="submit" onclick="disable(this);remove_dialog()" value="Submit" class="btn btn-primary">
</form>
<div id="message-preview-mobile" class="preview my-3"></div>
<div class="d-none mt-3 toggleable" id="coin-transfer-mobile">
<input autocomplete="off" id="coin-transfer-amount-mobile" class="form-control" name="amount" type="number" oninput="updateTax(true)">
<input autocomplete="off" id="coin-transfer-reason-mobile" maxlength=200 type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="coins-transfer-taxed-mobile">0</span> coins</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferCoins(this, true)">Gift</button>
</div>
<div class="d-none mt-3 toggleable" id="bux-transfer-mobile">
<input autocomplete="off" id="bux-transfer-amount-mobile" class="form-control" name="amount" type="number" oninput="updateBux(true)">
<input autocomplete="off" id="bux-transfer-reason-mobile" type="text" class="form-control" name="reason" placeholder="Gift message! (optional)">
<div>{{u.username}} will receive <span id="bux-transfer-taxed-mobile">0</span> marseybux</div>
<button type="button" class="btn btn-primary mt-2 mb-3" onclick="transferBux(this, true)">Gift</button>
</div>
{{userpage_admintools.userAdminTools('mobile')}}
{% endif %}
<div id="profile-mobile--info">
<p id="profile-mobile--info--id">User ID: {{u.id}}</p>
<p id="profile-mobile--info--spent">Coins spent: {{u.coins_spent}}</p>
<p id="profile-mobile--info--truescore">True score: {{u.truescore}}</p>
<p id="profile-mobile--info--winnings">Winnings: {{u.winnings}}</p>
<p id="profile-mobile--info--hats-owned" {% if u.num_of_owned_hats >= hats_total %}class="profile-owned-all-hats"{% endif %}>{{u.num_of_owned_hats}} / {{hats_total}} hats owned ({{hats_owned_percent}})</p>
{% if u.is_private %}
<p id="profile-mobile--info--private">User has private mode enabled</p>
{% endif %}
{% if v and (v.admin_level >= PERMS['VIEW_ALTS'] or v.alt) %}
{% if v.admin_level >= PERMS['USER_LINK'] %}
<span id="profile-mobile--alts"><a href="/@{{u.username}}/alts">Alts</a>:</span>
{% else %}
<span id="profile-mobile--alts">Alts:</span>
{% endif %}
<ul id="profile-mobile--alts-list">
{% for account in u.alts_unique if not account._alt_deleted and (v.can_see_shadowbanned or not account.shadowbanned) %}
<li><a href="{{account.url}}">@{{account.username}}</a>{% if account._is_manual %} [m]{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %} {% block content %}
{%- include 'userpage/banner.html' -%}
<div id="profilecontent" class="row no-gutters"> {%- include 'userpage/header.html' -%}
<div class="col"> {% block userpage_content required %}{% endblock %}
<div class="flex-row box-shadow-bottom d-flex justify-content-center justify-content-md-between align-items-center">
<ul class="nav settings-nav" id="profile-content--nav">
<li class="nav-item mr-1">
<a class="nav-link" href="/@{{u.username}}">Wall</a>
</li>
<li class="nav-item">
<a class="nav-link {% if not 'saved' in request.path %}active{% endif %}" href="/@{{u.username}}/posts">Posts <span class="count">({{u.post_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/comments">Comments <span class="count">({{u.comment_count}})</span></a>
</li>
{% if u.id == v.id %}
<li class="nav-item">
<a class="nav-link {% if '/saved/' in request.path %}active{% endif %}" href="/@{{u.username}}/saved/posts">Saved Posts <span class="count">({{u.saved_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/saved/comments">Saved Comments <span class="count">({{u.saved_comment_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link {% if '/subscribed/' in request.path %}active{% endif %}" href="/@{{u.username}}/subscribed/posts">Subscribed <span class="count">({{u.subscribed_count}})</span></a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
{% if "/saved/" not in request.path and '/subscribed/' not in request.path %}
<div class="d-flex justify-content-between align-items-center pt-4">
<div class="d-flex align-items-center">
<div class="dropdown dropdown-actions">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if t=="hour" %}<i class="fas fa-clock mr-1"></i>
{% elif t=="day" %}<i class="fas fa-calendar-day mr-1"></i>
{% elif t=="week" %}<i class="fas fa-calendar-week mr-1"></i>
{% elif t=="month" %}<i class="fas fa-calendar-alt mr-1"></i>
{% elif t=="year" %}<i class="fas fa-calendar mr-1"></i>
{% elif t=="all" %}<i class="fas fa-infinity mr-1"></i>
{% endif %}
{{t | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if t != "hour" %}<a class="dropdown-item" href="?sort={{sort}}&t=hour"><i class="fas fa-clock mr-2"></i>Hour</a>{% endif %}
{% if t != "day" %}<a class="dropdown-item" href="?sort={{sort}}&t=day"><i class="fas fa-calendar-day mr-2"></i>Day</a>{% endif %}
{% if t != "week" %}<a class="dropdown-item" href="?sort={{sort}}&t=week"><i class="fas fa-calendar-week mr-2"></i>Week</a>{% endif %}
{% if t != "month" %}<a class="dropdown-item" href="?sort={{sort}}&t=month"><i class="fas fa-calendar-alt mr-2"></i>Month</a>{% endif %}
{% if t != "year" %}<a class="dropdown-item" href="?sort={{sort}}&t=year"><i class="fas fa-calendar mr-2"></i>Year</a>{% endif %}
{% if t != "all" %}<a class="dropdown-item" href="?sort={{sort}}&t=all"><i class="fas fa-infinity mr-2"></i>All</a>{% endif %}
</div>
</div>
<div class="dropdown dropdown-actions ml-3">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton2" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if sort=="top" %}<i class="fas fa-arrow-alt-circle-up mr-1"></i>{% endif %}
{% if sort=="bottom" %}<i class="fas fa-arrow-alt-circle-down mr-1"></i>{% endif %}
{% if sort=="new" %}<i class="fas fa-sparkles mr-1"></i>{% endif %}
{% if sort=="old" %}<i class="fas fa-book mr-1"></i>{% endif %}
{% if sort=="controversial" %}<i class="fas fa-bullhorn mr-1"></i>{% endif %}
{% if sort=="comments" %}<i class="fas fa-comments mr-1"></i>{% endif %}
{{sort | capitalize}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton2" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 31px, 0px);">
{% if sort != "top" %}<a class="dropdown-item" href="?sort=top&t={{t}}"><i class="fas fa-arrow-alt-circle-up mr-2"></i>Top</a>{% endif %}
{% if sort != "bottom" %}<a class="dropdown-item" href="?sort=bottom&t={{t}}"><i class="fas fa-arrow-alt-circle-down mr-2"></i>Bottom</a>{% endif %}
{% if sort != "new" %}<a class="dropdown-item" href="?sort=new&t={{t}}"><i class="fas fa-sparkles mr-2"></i>New</a>{% endif %}
{% if sort != "old" %}<a class="dropdown-item" href="?sort=old&t={{t}}"><i class="fas fa-book mr-2"></i>Old</a>{% endif %}
{% if sort != "controversial" %}<a class="dropdown-item" href="?sort=controversial&t={{t}}"><i class="fas fa-bullhorn mr-2"></i>Controversial</a>{% endif %}
{% if sort != "comments" %}<a class="dropdown-item" href="?sort=comments&t={{t}}"><i class="fas fa-comments mr-2"></i>Comments</a>{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %}" style="margin-top: 10px;">
<div class="col">
<div class="posts">
{% include "submission_listing.html" %}
</div>
</div>
</div>
{% if FEATURES['USERS_PROFILE_SONG'] and u.song %} {% if FEATURES['USERS_PROFILE_SONG'] and u.song %}
{% if v and v.id == u.id %} {% if v and v.id == u.id %}
<div id="v_username" class="d-none">{{v.username}}</div> <div id="v_username" class="d-none">{{v.username}}</div>
@ -627,9 +31,7 @@
<script defer src="{{'js/userpage_v.js' | asset}}"></script> <script defer src="{{'js/userpage_v.js' | asset}}"></script>
<div id="username" class="d-none">{{u.username}}</div> <div id="username" class="d-none">{{u.username}}</div>
{% endif %} {% endif %}
<script defer src="{{'js/userpage.js' | asset}}"></script> <script defer src="{{'js/userpage.js' | asset}}"></script>
{% endblock %} {% endblock %}
{% block pagenav %} {% block pagenav %}
@ -662,7 +64,5 @@
{% if v and v.id != u.id and not request.path.endswith('/comments') and not request.path.endswith(u.username) %} {% if v and v.id != u.id and not request.path.endswith('/comments') and not request.path.endswith(u.username) %}
{% include "modals/emoji.html" %} {% include "modals/emoji.html" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block GIFpicker %}{% endblock %} {% block GIFpicker %}{% endblock %}

View File

@ -17,11 +17,8 @@
</a> </a>
</li> </li>
</ul> </ul>
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col"> <div class="col">
{% block listing %} {% block listing %}
<div class="posts"> <div class="posts">
{% include "submission_listing.html" %} {% include "submission_listing.html" %}

View File

@ -1,118 +1,24 @@
{% extends "userpage/userpage.html" %} {% extends "userpage/userpage.html" %}
{% block userpage_content %}
{% block content %} <div class="userpage-wall">
{{macros.comment_reply_box(u.fullname, 'replying-to-' + u.fullname, '', 'mb-3', '', true, '/comments/')}}
<div class="row no-gutters"> <div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %} px-3 p-md-0 userpage-wall" style="margin-top: 10px;">
<div class="col"> <div class="col">
<div class="flex-row box-shadow-bottom d-flex justify-content-center justify-content-md-between align-items-center"> <div class="comment-section" id="replies-of-{{u.fullname}}">
<ul class="nav settings-nav" id="profile-content--nav"> {% with comments=listing %}
<li class="nav-item mr-1"> {% include "comments.html" %}
<a class="nav-link active" href="/@{{u.username}}">Wall</a> {% endwith %}
</li> </div>
<li class="nav-item"> {% if not listing %}
<a class="nav-link" href="/@{{u.username}}/posts">Posts <span class="count">({{u.post_count}})</span></a> <div class="text-center px-3 my-3">
</li> <span class="fa-stack fa-2x text-muted mb-4">
<li class="nav-item"> <i class="fas fa-square text-gray-500 opacity-25 fa-stack-2x"></i>
<a class="nav-link" href="/@{{u.username}}/comments">Comments <span class="count">({{u.comment_count}})</span></a> <i class="fas text-gray-500 fa-scroll-old fa-stack-1x text-lg"></i>
</li> </span>
{% if u.id == v.id %} <h5>There's no comments on {% if u.id == v.id %}your{% else %}@{{u.username}}'s{% endif %} wall yet!</h5>
<li class="nav-item"> </div>
<a class="nav-link" href="/@{{u.username}}/saved/posts">Saved Posts <span class="count">({{u.saved_count}})</span></a> {% endif %}
</li> </div>
<li class="nav-item">
<a class="nav-link {% if 'saved' in request.path %}active{% endif %}" href="/@{{u.username}}/saved/comments">Saved Comments <span class="count">({{u.saved_comment_count}})</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/@{{u.username}}/subscribed/posts">Subscribed <span class="count">({{u.subscribed_count}})</span></a>
</li>
{% endif %}
</ul>
</div>
</div> </div>
</div> </div>
{% if v %}
<div id="comment-form-space-{{u.fullname}}" class="comment-write mb-3 mt-4 px-3 p-md-0">
<form id="reply-to-{{u.fullname}}" action="/comment" method="post">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<input type="hidden" name="parent_fullname" value="p_{{u.id}}">
<input autocomplete="off" id="reply-form-submission-{{u.fullname}}" type="hidden" name="submission" value="{{u.id}}">
<textarea required autocomplete="off" {% if v.longpost %}minlength="280"{% elif v.bird %}maxlength="140"{% endif %} minlength="1" maxlength="10000" data-preview="form-preview-{{u.fullname}}" oninput="markdown(this);charLimit('reply-form-body-{{u.fullname}}','charcount-{{u.fullname}}')" id="reply-form-body-{{u.fullname}}" data-fullname="{{u.fullname}}" class="comment-box form-control rounded" name="body" form="reply-to-{{u.fullname}}" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
<div class="text-small font-weight-bold mt-1" id="charcount-{{u.fullname}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
<div class="comment-format">
<label class="btn btn-secondary format d-inline-block m-0" for="gif-reply-btn-{{u.fullname}}">
<span id="gif-reply-btn-{{u.fullname}}" class="font-weight-bolder text-uppercase" onclick="commentForm('reply-form-body-{{u.fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span>
</label>
&nbsp;
<div onclick="loadEmojis('reply-form-body-{{u.fullname}}')" class="btn btn-secondary format d-inline-block m-0" id="emoji-reply-btn-{{u.fullname}}" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-smile-beam"></i></div>
&nbsp;
<label class="format btn btn-secondary m-0 ml-1 {% if v %}d-inline-block{% else %}d-none{% endif %}" for="file-upload-reply-{{u.fullname}}">
<div id="filename-show-reply-{{u.fullname}}"><i class="fas fa-file"></i></div>
<input autocomplete="off" id="file-upload-reply-{{u.fullname}}" accept="image/*, video/*, audio/*" type="file" multiple="multiple" name="file" {% if g.is_tor %}disabled{% endif %} onchange="changename('filename-show-reply-{{u.fullname}}','file-upload-reply-{{u.fullname}}')" hidden>
</label>
</div>
<button type="button" id="save-reply-to-{{u.fullname}}" form="reply-to-{{u.fullname}}" class="btn btn-primary text-whitebtn ml-auto fl-r" onclick="post_comment('{{u.fullname}}', '{{u.id}}');remove_dialog()">Comment</button>
</form>
<div id="form-preview-{{u.fullname}}" class="preview mb-3 mt-5"></div>
<div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div>
</div>
{% else %}
<div class="comment-write mb-3 mt-4">
<textarea autocomplete="off" maxlength="10000" class="comment-box form-control rounded" name="body" aria-label="With textarea" placeholder="Add your comment..." rows="3" onclick="location.href='/login?redirect={{request.full_path | urlencode}}';"></textarea>
</div>
<div class="card border-0 mt-4">
<div class="card-body">
<h5 class="card-title">Jump in the discussion.</h5>
<p class="card-text">No email address required.</p>
<div>
<a href="/signup?redirect={{request.full_path | urlencode}}" class="btn btn-primary">Sign up</a>
<a href="/login?redirect={{request.full_path | urlencode}}" class="btn btn-link text-muted">Sign in</a>
</div>
</div>
</div>
{% endif %}
{% if comment_info %}
<div class="total mt-2"><a href="/@{{u.username}}">View entire wall</a></div>
{% endif %}
<div class="row no-gutters {% if listing %}mt-md-3{% elif not listing %}my-md-3{% endif %} px-3 p-md-0" style="margin-top: 10px;">
<div class="col">
<div class="comment-section" id="replies-of-{{u.fullname}}">
{% with comments=listing %}
{% include "comments.html" %}
{% endwith %}
</div>
{% if not listing %}
<div class="text-center px-3 my-3">
<span class="fa-stack fa-2x text-muted mb-4">
<i class="fas fa-square text-gray-500 opacity-25 fa-stack-2x"></i>
<i class="fas text-gray-500 fa-scroll-old fa-stack-1x text-lg"></i>
</span>
<h5>There's no comments on your wall yet!</h5>
</div>
{% endif %}
</div>
</div>
{% if u.song %}
{% if v and v.id == u.id %}
<div id="v_username" class="d-none">{{v.username}}</div>
{% else %}
<div id="u_username" class="d-none">{{u.username}}</div>
{% endif %}
{% endif %}
{% if v %}
<div id='tax' class="d-none">{% if v.patron or u.patron %}0{% else %}0.03{% endif %}</div>
<div id="username" class="d-none">{{u.username}}</div>
<script defer src="{{'js/userpage_v.js' | asset}}"></script>
{% endif %}
<script defer src="{{'js/userpage.js' | asset}}"></script>
{% endblock %} {% endblock %}

View File

@ -109,3 +109,52 @@
{% endif %} {% endif %}
<span class="ml-2">{{p.views}} thread views</span> <span class="ml-2">{{p.views}} thread views</span>
{% endmacro %} {% endmacro %}
{% macro comment_reply_box(target_fullname, html_id, wrapper_css_classes="", subwrapper_css_classes="", hide="", allow_file_upload=true, action="/comments/") %}
<div class="comment-box-wrapper{% if wrapper_css_classes %} {{wrapper_css_classes}}{% endif %}" id="{{html_id}}">
{% if v %}
<div id="comment-form-space-{{target_fullname}}" class="comment-write {{subwrapper_css_classes}}">
<form id="reply-to-{{target_fullname}}" action="{{action}}" method="post">
<input type="hidden" name="formkey" value="{{v|formkey}}">
<input type="hidden" name="parent_fullname" value="{target_fullname}}">
<textarea required autocomplete="off" {% if not (p and p.id in ADMIGGER_THREADS) %}{% if v.longpost %}minlength="280"{% elif v.bird %}maxlength="140"{% endif %}{% endif %} minlength="1" maxlength="10000" data-preview="form-preview-{{target_fullname}}" oninput="markdown(this);charLimit('reply-form-body-{{target_fullname}}','charcount-{{target_fullname}}')" id="reply-form-body-{{target_fullname}}" data-fullname="{{target_fullname}}" class="comment-box form-control rounded" name="body" form="reply-to-{{target_fullname}}" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
<div class="text-small font-weight-bold mt-1" id="charcount-{{target_fullname}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
<div class="comment-format">
<label class="btn btn-secondary format d-inline-block m-0" for="gif-reply-btn-{{target_fullname}}">
<span id="gif-reply-btn-{{target_fullname}}" class="font-weight-bolder text-uppercase" onclick="commentForm('reply-form-body-{{target_fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span>
</label>
&nbsp;
<div onclick="loadEmojis('reply-form-body-{{target_fullname}}')" class="btn btn-secondary format d-inline-block m-0" id="emoji-reply-btn-{{target_fullname}}" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-smile-beam"></i></div>
&nbsp;
{% if allow_file_upload %}
<label class="format btn btn-secondary m-0 ml-1 {% if v %}d-inline-block{% else %}d-none{% endif %}" for="file-upload-reply-{{target_fullname}}">
<div id="filename-show-reply-{{target_fullname}}"><i class="fas fa-file"></i></div>
<input autocomplete="off" id="file-upload-reply-{{target_fullname}}" accept="image/*, video/*, audio/*" type="file" multiple="multiple" name="file" {% if g.is_tor %}disabled{% endif %} onchange="changename('filename-show-reply-{{target_fullname}}','file-upload-reply-{{target_fullname}}')" hidden>
</label>
{% endif %}
</div>
<button type="button" id="save-reply-to-{{target_fullname}}" form="reply-to-{{target_fullname}}" class="btn btn-primary text-whitebtn ml-auto fl-r" onclick="postComment('{{target_fullname}}', '{{hide}}');remove_dialog();">Comment</button>
</form>
<div id="form-preview-{{target_fullname}}" class="preview mb-3 mt-5"></div>
<div class="form-text text-small p-0 m-0"><a href="/formatting" {% if v and v.newtab %}data-target="t"target="_blank"{% endif %}>Formatting help</a></div>
</div>
{% else %}
<div class="comment-write mb-3">
<textarea autocomplete="off" maxlength="10000" class="comment-box form-control rounded" name="body" aria-label="With textarea" placeholder="Add your comment..." rows="3" onclick="location.href='/login?redirect={{request.full_path | urlencode}}';"></textarea>
</div>
<div class="card border-0 mt-4">
<div class="card-body">
<h5 class="card-title">Jump in the discussion.</h5>
<p class="card-text">No email address required.</p>
<div>
<a href="/signup?redirect={{request.full_path | urlencode}}" class="btn btn-primary">Sign up</a>
<a href="/login?redirect={{request.full_path | urlencode}}" class="btn btn-link text-muted">Sign in</a>
</div>
</div>
</div>
{% endif %}
</div>
{% endmacro %}