diff --git a/files/assets/css/main.css b/files/assets/css/main.css index f55407cdd..7883c46ba 100644 --- a/files/assets/css/main.css +++ b/files/assets/css/main.css @@ -5396,7 +5396,7 @@ textarea { } } .in-comment-image, .img, img[alt^="![]("], -.preview img:not(img[src^="/uid/"], img[src^="/pp/"], img[src$="/pic"], img[src$="/i/hand.webp"], img[src*="/e/"]) { +.preview img:not(img[src^="/uid/"], img[src^="/pp/"], img[src$="/pic"], img[src$="/i/hand.webp"], img[src$="/i/talking.webp"], img[src*="/e/"]) { max-height: 150px !important; max-width: 100% !important; border-radius: 0.2rem !important; @@ -5533,6 +5533,10 @@ img[src$="/i/hand.webp"] { margin-top: -10%; z-index: 1; } +img[src$="/i/talking.webp"] { + position: absolute; + width: 70%; +} img[src$="/i/hand.webp"]+img { animation: pat-pfp-anim 0.3s infinite; transform-origin: bottom center; @@ -5540,7 +5544,24 @@ img[src$="/i/hand.webp"]+img { text-align: center; object-fit: contain; } -img[src$="/i/hand.webp"]+img[src^="/pp/"], img[src$="/i/hand.webp"]+img[src$="/pic"] { +img[src$="/i/hand.webp"]+img[src$="/i/talking.webp"] { + padding-top: 20%; + padding-right: 5%; +} +img[src$="/i/hand.webp"]+img[src$="/i/talking.webp"]+img { + animation: pat-pfp-anim 0.3s infinite; + transform-origin: bottom center; + margin-top: 20%; + text-align: center; + object-fit: contain; +} +img[src$="/i/talking.webp"]+img { + padding-top: 22%; +} +img[src$="/i/talking.webp"]+img[src$="/i/hand.webp"]+img { + padding-top: 20%; +} +img[src$="/i/hand.webp"]+img[src^="/pp/"], img[src$="/i/hand.webp"]+img[src$="/pic"], img[src$="/i/talking.webp"]+img[src$="/pic"] { border-radius: 50%; } @keyframes pat-pfp-anim { diff --git a/files/assets/images/talking.webp b/files/assets/images/talking.webp new file mode 100644 index 000000000..c0b2fffc8 Binary files /dev/null and b/files/assets/images/talking.webp differ diff --git a/files/assets/js/markdown.js b/files/assets/js/markdown.js index c45850293..0e2627ca3 100644 --- a/files/assets/js/markdown.js +++ b/files/assets/js/markdown.js @@ -65,6 +65,14 @@ function replace_image(match, prefix, url) { return match } +const MODIFIERS = { + PAT: 1, + TALKING: 2, + LARGE: 3, + REVERSED: 4, + USER: 5 +}; + function markdown(t) { let input = t.value; @@ -95,14 +103,46 @@ function markdown(t) { if(emojis != null){ for(i = 0; i < emojis.length; i++){ const old = emojis[i][0]; - if (old.includes('marseyrandom')) continue - let emoji = old.replace(/[:!@#]/g,'').toLowerCase(); - const mirroredClass = old.indexOf('!') == -1 ? '' : 'mirrored'; - const emojiClass = old.indexOf('#') == -1 ? 'emoji' : 'emoji-lg'; - if (emoji.endsWith('pat') && emoji != 'marseyunpettablepat') { - emoji = emoji.substr(0, emoji.length - 3); - const url = old.indexOf('@') != -1 ? `/@${emoji}/pic` : `${SITE_FULL_IMAGES}/e/${emoji}.webp`; - input = input.replace(old, ``); + if (old.includes('marseyrandom')) continue; + if (old.includes('marseyrandom')) continue + let emoji = old.replace(/[:]/g,'').toLowerCase(); + + const modifiers = new Set(); + const isTalkingFirst = !emoji.endsWith('pat') && emoji.endsWith('talking', -3); + if(emoji.endsWith('talking') || (emoji.endsWith('pat') && emoji.slice(0, -3).endsWith('talking'))) { + modifiers.add(MODIFIERS.TALKING); + emoji = emoji.endsWith('pat') ? [emoji.slice(0, -10), emoji.slice(-3)].join('') : emoji.slice(0, -7); + } + if(emoji.endsWith('pat')) { + modifiers.add(MODIFIERS.PAT); + emoji = emoji.slice(0, -3); + } + let length = emoji.length + emoji = emoji.startsWith('@', '') ? emoji.slice(1): emoji; + if(length !== emoji.length) { + modifiers.add(MODIFIERS.USER); + length = emoji.length + } + emoji = emoji.replaceAll('!', ''); + if(length !== emoji.length) { + modifiers.add(MODIFIERS.REVERSED); + length = emoji.length; + } + emoji = emoji.replaceAll('#', ''); + if(length !== emoji.length) { + modifiers.add(MODIFIERS.LARGE); + } + + + const mirroredClass = modifiers.has(MODIFIERS.REVERSED) ? 'mirrored' : ''; + const emojiClass = modifiers.has(MODIFIERS.LARGE) ? 'emoji-lg' : 'emoji'; + + if (modifiers.has(MODIFIERS.PAT) || modifiers.has(MODIFIERS.TALKING)) { + const talkingHtml = modifiers.has(MODIFIERS.TALKING) ? `` : ''; + const patHtml = modifiers.has(MODIFIERS.PAT) ? `` : ''; + const url = modifiers.has(MODIFIERS.USER) ? `/@${emoji}/pic` : `${SITE_FULL_IMAGES}/e/${emoji}.webp`; + const modifierHtml = isTalkingFirst ? `${talkingHtml}${patHtml}` : `${patHtml}${talkingHtml}`; + input = input.replace(old, `${modifierHtml}`); } else { input = input.replace(old, ``); } diff --git a/files/classes/user.py b/files/classes/user.py index 563b24ad3..93f2eb61f 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -281,7 +281,7 @@ class User(Base): @lazy def hats_owned_proportion_display(self): total_num_of_hats = g.db.query(HatDef).filter(HatDef.submitter_id == None, HatDef.price > 0).count() - proportion = f'{float(self.num_of_owned_hats) / total_num_of_hats:.1%}' + proportion = f'{float(self.num_of_owned_hats) / total_num_of_hats:.1%}' if total_num_of_hats > 0 else 'N/A' return (proportion, total_num_of_hats) @property diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py index eb41e2faf..37a54fc72 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -245,12 +245,33 @@ def render_emoji(html, regexp, golden, emojis_used, b=False, is_title=False): emoji_partial = ':{0}:' emoji_html = None - if emoji.endswith('pat') and emoji != 'marseyunpettablepat': - if path.isfile(f"files/assets/images/emojis/{emoji.replace('pat','')}.webp"): - emoji_html = f'{emoji_partial_pat.format(old, f"{SITE_FULL_IMAGES}/e/{emoji[:-3]}.webp", attrs)}' - elif emoji.startswith('@'): - if u := get_user(emoji[1:-3], graceful=True): - emoji_html = f'{emoji_partial_pat.format(old, f"/pp/{u.id}", attrs)}' + is_talking = emoji.endswith('talking') or (emoji[:-3].endswith('talking') and emoji.endswith('pat')) + is_talking_first = emoji.endswith('talking') + emoji = emoji.replace('talking', '') if is_talking else emoji + is_patted = emoji.endswith('pat') + emoji = emoji.replace('pat', '') + is_user = emoji.startswith('@') + + end_modifier_length = 3 if is_patted else 0 + end_modifier_length = end_modifier_length + 7 if is_talking else end_modifier_length + + hand_html = f'' if is_patted and emoji != 'marseyunpettablepat' else '' + talking_html = f'' if is_talking else '' + + modifier_html = None + if (is_talking and is_patted): + modifier_html = f'{talking_html}{hand_html}' if is_talking_first else f'{hand_html}{talking_html}' + elif (is_patted): + modifier_html = hand_html + elif (is_talking): + modifier_html = talking_html + + if (is_patted and emoji != 'marseyunpettablepat') or is_talking: + if path.isfile(f"files/assets/images/emojis/{emoji}.webp"): + emoji_html = f'{modifier_html}{emoji_partial_pat.format(old, f"{SITE_FULL_IMAGES}/e/{emoji}.webp", attrs)}' + elif is_user: + if u := get_user(emoji[1:], graceful=True): + emoji_html = f'{modifier_html}{emoji_partial_pat.format(old, f"/pp/{u.id}", attrs)}' elif path.isfile(f'files/assets/images/emojis/{emoji}.webp'): emoji_html = emoji_partial.format(old, f'{SITE_FULL_IMAGES}/e/{emoji}.webp', attrs)