notifications rework

remotes/1693045480750635534/spooky-22
Aevann1 2022-07-08 20:06:54 +02:00
parent cfa6c13eaa
commit ad1273995d
12 changed files with 296 additions and 215 deletions

View File

@ -91,6 +91,8 @@ def before_request():
g.webview = '; wv) ' in ua
g.inferior_browser = 'iphone' in ua or 'ipad' in ua or 'ipod' in ua or 'mac os' in ua or ' firefox/' in ua
if request.path.endswith('/'): request.path = request.path[:-1]
@app.after_request
def after_request(response):
response.headers.add("Strict-Transport-Security", "max-age=31536000")

View File

@ -127,6 +127,7 @@ class User(Base):
currently_held_lottery_tickets = Column(Integer, default=0)
total_held_lottery_tickets = Column(Integer, default=0)
total_lottery_winnings = Column(Integer, default=0)
last_viewed_post_notifs = Column(Integer, default=0)
badges = relationship("Badge", order_by="Badge.created_utc", back_populates="user")
subscriptions = relationship("Subscription", back_populates="user")
@ -447,6 +448,11 @@ class User(Base):
if self.admin_level < 2: return 0
return g.db.query(ModAction).filter_by(user_id=self.id).count()
@property
@lazy
def following_ids(self):
return [x[0] for x in g.db.query(Follow.target_id).filter_by(user_id=self.id).all()]
@property
@lazy
def notifications_count(self):
@ -457,7 +463,7 @@ class User(Base):
if not self.shadowbanned and self.admin_level < 3:
notifs = notifs.join(Notification.user).filter(User.shadowbanned == None)
return notifs.count()
return notifs.count() + self.post_notifications_count
@property
@lazy
@ -470,9 +476,7 @@ class User(Base):
@property
@lazy
def post_notifications_count(self):
return g.db.query(Notification).join(Comment).filter(
Notification.user_id == self.id, Notification.read == False,
Comment.author_id == AUTOJANNY_ID).count()
return g.db.query(Submission).filter(Submission.author_id.in_(self.following_ids), Submission.created_utc > self.last_viewed_post_notifs).count()
@property
@lazy

View File

@ -58,4 +58,4 @@ def inject_constants():
"HOLE_NAME": HOLE_NAME, "HOLE_STYLE_FLAIR": HOLE_STYLE_FLAIR, "HOLE_REQUIRED": HOLE_REQUIRED,
"LOTTERY_ENABLED": LOTTERY_ENABLED, "GUMROAD_LINK": GUMROAD_LINK,
"DEFAULT_THEME": DEFAULT_THEME, "DESCRIPTION": DESCRIPTION, "PERMS": PERMS,
"PROCOINS_ENABLED": PROCOINS_ENABLED, "has_sidebar": has_sidebar, "has_logo": has_logo, "FP": FP}
"PROCOINS_ENABLED": PROCOINS_ENABLED, "has_sidebar": has_sidebar, "has_logo": has_logo, "FP": FP, "NOTIF_MODACTION_JL_MIN": NOTIF_MODACTION_JL_MIN}

View File

@ -17,4 +17,5 @@ from .awards import *
from .giphy import *
from .subs import *
from .lottery import *
from .polls import *
from .polls import *
from .notifications import *

View File

@ -6,175 +6,6 @@ from files.__main__ import app, cache, limiter
from files.classes.submission import Submission
from files.helpers.awards import award_timers
@app.post("/clear")
@auth_required
def clear(v):
notifs = g.db.query(Notification).join(Notification.comment).filter(Notification.read == False, Notification.user_id == v.id).all()
for n in notifs:
n.read = True
g.db.add(n)
return {"message": "Notifications cleared!"}
@app.get("/unread")
@auth_required
def unread(v):
listing = g.db.query(Notification, Comment).join(Notification.comment).filter(
Notification.read == False,
Notification.user_id == v.id,
Comment.is_banned == False,
Comment.deleted_utc == 0,
Comment.author_id != AUTOJANNY_ID,
).order_by(Notification.created_utc.desc()).all()
for n, c in listing:
n.read = True
g.db.add(n)
return {"data":[x[1].json for x in listing]}
@app.get("/notifications")
@auth_required
def notifications(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
messages = request.values.get('messages')
modmail = request.values.get('modmail')
modactions = request.values.get('modactions')
posts = request.values.get('posts')
reddit = request.values.get('reddit')
if modmail and v.admin_level >= 2:
comments = g.db.query(Comment).filter(Comment.sentto==2).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists = (len(comments) > 25)
listing = comments[:25]
elif messages:
if v and (v.shadowbanned or v.admin_level > 2):
comments = g.db.query(Comment).filter(Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
else:
comments = g.db.query(Comment).join(Comment.author).filter(User.shadowbanned == None, Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists = (len(comments) > 25)
listing = comments[:25]
elif posts:
notifications = g.db.query(Notification, Comment).join(Notification.comment).filter(Notification.user_id == v.id, Comment.author_id == AUTOJANNY_ID).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing))
elif modactions:
notifications = g.db.query(Notification, Comment) \
.join(Notification.comment) \
.filter(Notification.user_id == v.id,
Comment.body_html.like(f'%<p>{NOTIF_MODACTION_PREFIX}%'),
Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID) \
.order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing))
elif reddit:
notifications = g.db.query(Notification, Comment).join(Notification.comment).filter(Notification.user_id == v.id, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing))
else:
comments = g.db.query(Comment, Notification).join(Notification.comment).filter(
Notification.user_id == v.id,
Comment.is_banned == False,
Comment.deleted_utc == 0,
Comment.author_id != AUTOJANNY_ID,
Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%'),
Comment.body_html.notlike(f'%<p>{NOTIF_MODACTION_PREFIX}%')
).order_by(Notification.created_utc.desc())
if not (v and (v.shadowbanned or v.admin_level > 2)):
comments = comments.join(Comment.author).filter(User.shadowbanned == None)
comments = comments.offset(25 * (page - 1)).limit(26).all()
next_exists = (len(comments) > 25)
comments = comments[:25]
cids = [x[0].id for x in comments]
comms = get_comments(cids, v=v)
listing = []
for c, n in comments:
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
if not n.read:
n.read = True
c.unread = True
g.db.add(n)
if c.parent_submission:
if c.replies2 == None:
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
for x in c.replies2:
if x.replies2 == None: x.replies2 = []
count = 0
while count < 50 and c.parent_comment and (c.parent_comment.author_id == v.id or c.parent_comment.id in cids):
count += 1
c = c.parent_comment
if c.replies2 == None:
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
for x in c.replies2:
if x.replies2 == None:
x.replies2 = g.db.query(Comment).filter_by(parent_comment_id=x.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
else:
while c.parent_comment:
c = c.parent_comment
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).order_by(Comment.id).all()
if c not in listing: listing.append(c)
g.db.commit()
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
NOTIF_MODACTION_JL_MIN=NOTIF_MODACTION_JL_MIN,
)
@app.get("/")
@app.get("/catalog")
@app.get("/h/<sub>")

View File

@ -0,0 +1,258 @@
from files.helpers.wrappers import *
from files.helpers.get import *
from files.helpers.const import *
from files.__main__ import app
@app.post("/clear")
@auth_required
def clear(v):
notifs = g.db.query(Notification).join(Notification.comment).filter(Notification.read == False, Notification.user_id == v.id).all()
for n in notifs:
n.read = True
g.db.add(n)
return {"message": "Notifications cleared!"}
@app.get("/unread")
@auth_required
def unread(v):
listing = g.db.query(Notification, Comment).join(Notification.comment).filter(
Notification.read == False,
Notification.user_id == v.id,
Comment.is_banned == False,
Comment.deleted_utc == 0,
Comment.author_id != AUTOJANNY_ID,
).order_by(Notification.created_utc.desc()).all()
for n, c in listing:
n.read = True
g.db.add(n)
return {"data":[x[1].json for x in listing]}
@app.get("/notifications/modmail")
@admin_level_required(2)
def notifications_modmail(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
comments = g.db.query(Comment).filter(Comment.sentto==2).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists = (len(comments) > 25)
listing = comments[:25]
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)
@app.get("/notifications/messages")
@auth_required
def notifications_messages(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
if v and (v.shadowbanned or v.admin_level > 2):
comments = g.db.query(Comment).filter(Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
else:
comments = g.db.query(Comment).join(Comment.author).filter(User.shadowbanned == None, Comment.sentto != None, or_(Comment.author_id==v.id, Comment.sentto==v.id), Comment.parent_submission == None, Comment.level == 1).order_by(Comment.id.desc()).offset(25*(page-1)).limit(26).all()
next_exists = (len(comments) > 25)
listing = comments[:25]
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)
@app.get("/notifications/posts")
@auth_required
def notifications_posts(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
listing = g.db.query(Submission).filter(Submission.author_id.in_(v.following_ids)).order_by(Submission.created_utc.desc()).offset(25 * (page - 1)).limit(26).all()
next_exists = (len(listing) > 25)
listing = listing[:25]
for p in listing:
p.unread = p.created_utc > v.last_viewed_post_notifs
v.last_viewed_post_notifs = int(time.time())
g.db.add(v)
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)
@app.get("/notifications/modactions")
@admin_level_required(NOTIF_MODACTION_JL_MIN)
def notifications_modactions(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
notifications = g.db.query(Notification, Comment) \
.join(Notification.comment) \
.filter(Notification.user_id == v.id,
Comment.body_html.like(f'%<p>{NOTIF_MODACTION_PREFIX}%'),
Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID) \
.order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing))
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)
@app.get("/notifications/reddit")
@auth_required
def notifications_reddit(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
if not v.can_view_offsitementions: abort(403)
notifications = g.db.query(Notification, Comment).join(Notification.comment).filter(Notification.user_id == v.id, Comment.body_html.like('%<p>New site mention: <a href="https://old.reddit.com/r/%'), Comment.parent_submission == None, Comment.author_id == NOTIFICATIONS_ID).order_by(Notification.created_utc.desc()).offset(25 * (page - 1)).limit(101).all()
listing = []
for index, x in enumerate(notifications[:100]):
n, c = x
if n.read and index > 24: break
elif not n.read:
n.read = True
c.unread = True
g.db.add(n)
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
listing.append(c)
next_exists = (len(notifications) > len(listing))
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)
@app.get("/notifications")
@auth_required
def notifications(v):
try: page = max(int(request.values.get("page", 1)), 1)
except: page = 1
comments = g.db.query(Comment, Notification).join(Notification.comment).filter(
Notification.user_id == v.id,
Comment.is_banned == False,
Comment.deleted_utc == 0,
Comment.author_id != AUTOJANNY_ID,
Comment.body_html.notlike('%<p>New site mention: <a href="https://old.reddit.com/r/%'),
Comment.body_html.notlike(f'%<p>{NOTIF_MODACTION_PREFIX}%')
).order_by(Notification.created_utc.desc())
if not (v and (v.shadowbanned or v.admin_level > 2)):
comments = comments.join(Comment.author).filter(User.shadowbanned == None)
comments = comments.offset(25 * (page - 1)).limit(26).all()
next_exists = (len(comments) > 25)
comments = comments[:25]
cids = [x[0].id for x in comments]
comms = get_comments(cids, v=v)
listing = []
for c, n in comments:
if n.created_utc > 1620391248: c.notif_utc = n.created_utc
if not n.read:
n.read = True
c.unread = True
g.db.add(n)
if c.parent_submission:
if c.replies2 == None:
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
for x in c.replies2:
if x.replies2 == None: x.replies2 = []
count = 0
while count < 50 and c.parent_comment and (c.parent_comment.author_id == v.id or c.parent_comment.id in cids):
count += 1
c = c.parent_comment
if c.replies2 == None:
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
for x in c.replies2:
if x.replies2 == None:
x.replies2 = g.db.query(Comment).filter_by(parent_comment_id=x.id).filter(or_(Comment.author_id == v.id, Comment.id.in_(cids))).all()
else:
while c.parent_comment:
c = c.parent_comment
c.replies2 = g.db.query(Comment).filter_by(parent_comment_id=c.id).order_by(Comment.id).all()
if c not in listing: listing.append(c)
if request.headers.get("Authorization"): return {"data":[x.json for x in listing]}
return render_template("notifications.html",
v=v,
notifications=listing,
next_exists=next_exists,
page=page,
standalone=True,
render_replies=True,
)

View File

@ -527,7 +527,7 @@ def thumbnail_thread(pid):
parsed_url = urlparse(post_url)
return f"https://{parsed_url.netloc}{fragment_url}"
else:
return f"{post_url}{'/' if not post_url.endswith('/') else ''}{fragment_url}"
return f"{post_url}/{fragment_url}"
post = db.get(Submission, pid)

View File

@ -688,7 +688,7 @@ def message2(v, username):
if len(message) > 500: notifbody = message[:500] + '...'
else: notifbody = message
url = f'{SITE_FULL}/notifications?messages=true'
url = f'{SITE_FULL}/notifications/messages'
gevent.spawn(pusher_thread, interests, title, notifbody, url)
@ -761,7 +761,7 @@ def messagereply(v):
if len(body) > 500: notifbody = body[:500] + '...'
else: notifbody = body
url = f'{SITE_FULL}/notifications?messages=true'
url = f'{SITE_FULL}/notifications/messages'
gevent.spawn(pusher_thread, interests, title, notifbody, url)

View File

@ -112,7 +112,7 @@
{% if v.notifications_count %}
<li class="nav-item d-flex align-items-center text-center justify-content-center mx-1">
<a class="nav-link position-relative" href="/notifications{% if v.notifications_do %}?{{v.notifications_do}}=true{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell" style="color: {{v.notifications_color}}"></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.notifications_do %}background:{{v.notifications_color}}{% endif %}">{{v.notifications_count}}</span></a>
<a class="nav-link position-relative" href="/notifications{% if v.notifications_do %}/{{v.notifications_do}}{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell" style="color: {{v.notifications_color}}"></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.notifications_do %}background:{{v.notifications_color}}{% endif %}">{{v.notifications_count}}</span></a>
</li>
{% else %}
<li class="nav-item d-flex align-items-center text-center justify-content-center mx-1">

View File

@ -16,37 +16,37 @@
<div class="col p-0 w-100">
<ul class="nav settings-nav" style="padding:0 0 0 20px" id="notifications--nav-list">
<li class="nav-item">
<a class="nav-link py-3{% if not '=true' in request.full_path %} active{% endif %}" href="/notifications">
<a class="nav-link py-3{% if request.path == '/notifications' %} active{% endif %}" href="/notifications">
All {% if v.normal_notifications_count %}<span class="font-weight-bold" style="color:#ff0000">({{v.normal_notifications_count}})</span>{% endif %}
</a>
</li>
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?posts=true' in request.full_path %} active{% endif %}" href="/notifications?posts=true">
<a class="nav-link py-3{% if request.path == '/notifications/posts' %} active{% endif %}" href="/notifications/posts">
Posts {% if v.post_notifications_count %}<span class="font-weight-bold" style="color:#0000ff">({{v.post_notifications_count}})</span>{% endif %}
</a>
</li>
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?messages=true' in request.full_path %} active{% endif %}" href="/notifications?messages=true">
<a class="nav-link py-3{% if request.path == '/notifications/messages' %} active{% endif %}" href="/notifications/messages">
Messages
</a>
</li>
{% if v.admin_level >= NOTIF_MODACTION_JL_MIN %}
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?modactions=true' in request.full_path %} active{% endif %}" href="/notifications?modactions=true">
<a class="nav-link py-3{% if request.path == '/notifications/modactions' %} active{% endif %}" href="/notifications/modactions">
Modactions {% if v.modaction_notifications_count %}<span class="font-weight-bold" style="color:#e5990d">({{v.modaction_notifications_count}})</span>{% endif %}
</a>
</li>
{% endif %}
{% if v.admin_level >= 2 %}
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?modmail=true' in request.full_path %} active{% endif %}" href="/notifications?modmail=true">
<a class="nav-link py-3{% if request.path == '/notifications/modmail' %} active{% endif %}" href="/notifications/modmail">
Modmail
</a>
</li>
{% endif %}
{% if v.can_view_offsitementions %}
<li class="nav-item">
<a class="nav-link py-3{% if '/notifications?reddit=true' in request.full_path %} active{% endif %}" href="/notifications?reddit=true">
<a class="nav-link py-3{% if request.path == '/notifications/reddit' %} active{% endif %}" href="/notifications/reddit">
Reddit {% if v.reddit_notifications_count %}<span class="font-weight-bold" style="color:#805ad5">({{v.reddit_notifications_count}})</span>{% endif %}
</a>
</li>
@ -55,14 +55,19 @@
</div>
</div>
<a class="btn btn-primary mt-3 ml-3" role="button" onclick="post_toast(this,'/clear', true)">Clear all notifications</a>
<div class="notifs px-3 p-md-0">
{% with comments=notifications %}
{% include "comments.html" %}
{% endwith %}
{% if request.path == '/notifications/posts' %}
{% with listing=notifications %}
{% include "submission_listing.html" %}
{% endwith %}
{% else %}
{% with comments=notifications %}
{% include "comments.html" %}
{% endwith %}
{% endif %}
{% if not notifications %}
<div class="text-center py-7 py-md-8">
@ -89,18 +94,7 @@
<ul class="pagination pagination-sm mb-0 mt-4">
{% if page>1 %}
<li class="page-item">
{% if "?page=" in request.full_path %}
{% set path = request.full_path.split("?page=")[0] %}
{% elif "&page=" in request.full_path %}
{% set path = request.full_path.split("&page=")[0] %}
{% else %}
{% set path = request.full_path %}
{% endif %}
{% if '=true' in request.full_path %}
<small><a class="page-link" href="{{path}}&page={{page-1}}">Previous</a></small>
{% else %}
<small><a class="page-link" href="{{request.path}}?page={{page-1}}">Previous</a></small>
{% endif %}
<small><a class="page-link" href="{{request.path}}?page={{page-1}}">Previous</a></small>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link">Prev</span></li>
@ -108,18 +102,7 @@
{% if next_exists %}
<li class="page-item">
{% if "?page=" in request.full_path %}
{% set path = request.full_path.split("?page=")[0] %}
{% elif "&page=" in request.full_path %}
{% set path = request.full_path.split("&page=")[0] %}
{% else %}
{% set path = request.full_path %}
{% endif %}
{% if '=true' in request.full_path %}
<small><a class="page-link" href="{{path}}&page={{page+1}}">Next</a></small>
{% else %}
<small><a class="page-link" href="{{request.path}}?page={{page+1}}">Next</a></small>
{% endif %}
<small><a class="page-link" href="{{request.path}}?page={{page+1}}">Next</a></small>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>

View File

@ -81,7 +81,7 @@
<div id="post-{{p.id}}" class="card{% if p.is_banned %} banned{% endif %}{% if p.deleted_utc %} deleted{% endif %}{% if p.stickied %} stickied{% endif %}{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}{% if p.over_18 %} nsfw{% endif %}">
<div class="d-flex flex-row-reverse flex-md-row flex-nowrap justify-content-end">
<div class="{% if p.unread %}unread{% endif %} d-flex flex-row-reverse flex-md-row flex-nowrap justify-content-end">
{% if not postembed %}
<div class="voting my-2 d-none d-md-block pr-2">
@ -421,7 +421,7 @@
</div>
{% endif %}
{% else %}
{% elif request.path != '/notifications/posts' %}
<div class="row no-gutters">
<div class="col">
<div class="text-center px-3 my-3">

View File

@ -0,0 +1,2 @@
alter table users add column last_viewed_post_notifs int not null default 0;
alter table users alter column last_viewed_post_notifs drop default;