re-add subscriptions
This commit is contained in:
parent
52f6484db1
commit
29bb9872d3
@ -105,4 +105,11 @@ def create_app():
|
|||||||
def ts_datetime(ts, format):
|
def ts_datetime(ts, format):
|
||||||
return datetime.utcfromtimestamp(ts or int(time.time())).strftime(format)
|
return datetime.utcfromtimestamp(ts or int(time.time())).strftime(format)
|
||||||
|
|
||||||
|
@app.template_filter("pluralize")
|
||||||
|
def pluralize(number, singular = "", plural = "s"):
|
||||||
|
if number == 1:
|
||||||
|
return singular
|
||||||
|
|
||||||
|
return plural
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -56,6 +56,18 @@ class Users(Model):
|
|||||||
WHERE users.id = ?"""
|
WHERE users.id = ?"""
|
||||||
return db.fetch_one(q, self.id)
|
return db.fetch_one(q, self.id)
|
||||||
|
|
||||||
|
def get_all_subscriptions(self):
|
||||||
|
q = """
|
||||||
|
SELECT threads.title AS thread_title, threads.slug AS thread_slug
|
||||||
|
FROM
|
||||||
|
threads
|
||||||
|
JOIN
|
||||||
|
subscriptions ON subscriptions.thread_id = threads.id
|
||||||
|
WHERE
|
||||||
|
subscriptions.user_id = ?"""
|
||||||
|
return db.query(q, self.id)
|
||||||
|
|
||||||
|
|
||||||
class Topics(Model):
|
class Topics(Model):
|
||||||
table = "topics"
|
table = "topics"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from flask import (
|
|||||||
)
|
)
|
||||||
from .users import login_required, mod_only, get_active_user, is_logged_in
|
from .users import login_required, mod_only, get_active_user, is_logged_in
|
||||||
from ..db import db
|
from ..db import db
|
||||||
from ..models import Threads, Topics, Posts
|
from ..models import Threads, Topics, Posts, Subscriptions
|
||||||
from .posts import create_post
|
from .posts import create_post
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
import math
|
import math
|
||||||
@ -39,7 +39,17 @@ def thread(slug):
|
|||||||
topic = Topics.find({"id": thread.topic_id})
|
topic = Topics.find({"id": thread.topic_id})
|
||||||
other_topics = Topics.select()
|
other_topics = Topics.select()
|
||||||
|
|
||||||
#TODO: subscription last seen
|
is_subscribed = False
|
||||||
|
if is_logged_in():
|
||||||
|
subscription = Subscriptions.find({
|
||||||
|
'thread_id': thread.id,
|
||||||
|
'user_id': get_active_user().id,
|
||||||
|
})
|
||||||
|
if subscription:
|
||||||
|
subscription.update({
|
||||||
|
'last_seen': int(time.time())
|
||||||
|
})
|
||||||
|
is_subscribed = True
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"threads/thread.html",
|
"threads/thread.html",
|
||||||
@ -49,6 +59,7 @@ def thread(slug):
|
|||||||
posts = posts,
|
posts = posts,
|
||||||
topic = topic,
|
topic = topic,
|
||||||
topics = other_topics,
|
topics = other_topics,
|
||||||
|
is_subscribed = is_subscribed,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -122,3 +133,39 @@ def sticky(slug):
|
|||||||
@mod_only(".thread", slug = lambda slug: slug)
|
@mod_only(".thread", slug = lambda slug: slug)
|
||||||
def move(slug):
|
def move(slug):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post("/<slug>/subscribe")
|
||||||
|
@login_required
|
||||||
|
def subscribe(slug):
|
||||||
|
user = get_active_user()
|
||||||
|
thread = Threads.find({'slug': slug})
|
||||||
|
if not thread:
|
||||||
|
return 'no'
|
||||||
|
subscription = Subscriptions.find({
|
||||||
|
'user_id': user.id,
|
||||||
|
'thread_id': thread.id,
|
||||||
|
})
|
||||||
|
if request.form['subscribe'] == 'subscribe':
|
||||||
|
if subscription:
|
||||||
|
subscription.delete()
|
||||||
|
Subscriptions.create({
|
||||||
|
'user_id': user.id,
|
||||||
|
'thread_id': thread.id,
|
||||||
|
'last_seen': int(time.time()),
|
||||||
|
})
|
||||||
|
elif request.form['subscribe'] == 'unsubscribe':
|
||||||
|
if not subscription:
|
||||||
|
return 'no'
|
||||||
|
subscription.delete()
|
||||||
|
elif request.form['subscribe'] == 'read':
|
||||||
|
if not subscription:
|
||||||
|
return 'no'
|
||||||
|
subscription.update({
|
||||||
|
'last_seen': int(time.time())
|
||||||
|
})
|
||||||
|
last_visible_post = request.form.get('last_visible_post', default=None)
|
||||||
|
if last_visible_post is not None:
|
||||||
|
return redirect(url_for('.thread', slug=thread.slug, after=last_visible_post))
|
||||||
|
else:
|
||||||
|
return redirect(url_for('users.inbox', username=user.username))
|
||||||
|
@ -2,7 +2,8 @@ from flask import (
|
|||||||
Blueprint, render_template, request, redirect, url_for, flash, session, current_app
|
Blueprint, render_template, request, redirect, url_for, flash, session, current_app
|
||||||
)
|
)
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from ..models import Users, Sessions
|
from ..db import db
|
||||||
|
from ..models import Users, Sessions, Subscriptions
|
||||||
from ..constants import InfoboxKind, PermissionLevel
|
from ..constants import InfoboxKind, PermissionLevel
|
||||||
from ..auth import digest, verify
|
from ..auth import digest, verify
|
||||||
import secrets
|
import secrets
|
||||||
@ -202,12 +203,6 @@ def settings(username):
|
|||||||
return "stub"
|
return "stub"
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/<username>/inbox")
|
|
||||||
@login_required
|
|
||||||
def inbox(username):
|
|
||||||
return "stub"
|
|
||||||
|
|
||||||
|
|
||||||
@bp.post("/log_out")
|
@bp.post("/log_out")
|
||||||
@login_required
|
@login_required
|
||||||
def log_out():
|
def log_out():
|
||||||
@ -282,3 +277,86 @@ def guest_user(user_id):
|
|||||||
"permission": PermissionLevel.GUEST.value,
|
"permission": PermissionLevel.GUEST.value,
|
||||||
})
|
})
|
||||||
return redirect(url_for(".page", username=target_user.username))
|
return redirect(url_for(".page", username=target_user.username))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("/<username>/inbox")
|
||||||
|
@login_required
|
||||||
|
def inbox(username):
|
||||||
|
user = get_active_user()
|
||||||
|
if username != user.username:
|
||||||
|
return redirect(url_for(".inbox", username = user.username))
|
||||||
|
|
||||||
|
new_posts = []
|
||||||
|
subscription = Subscriptions.find({"user_id": user.id})
|
||||||
|
all_subscriptions = None
|
||||||
|
total_unreads_count = None
|
||||||
|
if subscription:
|
||||||
|
all_subscriptions = user.get_all_subscriptions()
|
||||||
|
q = """
|
||||||
|
WITH thread_metadata AS (
|
||||||
|
SELECT
|
||||||
|
posts.thread_id, threads.slug AS thread_slug, threads.title AS thread_title, COUNT(*) AS unread_count, MAX(posts.created_at) AS newest_post_time
|
||||||
|
FROM
|
||||||
|
posts
|
||||||
|
LEFT JOIN
|
||||||
|
threads ON threads.id = posts.thread_id
|
||||||
|
LEFT JOIN
|
||||||
|
subscriptions ON subscriptions.thread_id = posts.thread_id
|
||||||
|
WHERE subscriptions.user_id = ? AND posts.created_at > subscriptions.last_seen
|
||||||
|
GROUP BY posts.thread_id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
tm.thread_id, tm.thread_slug, tm.thread_title, tm.unread_count, tm.newest_post_time,
|
||||||
|
|
||||||
|
posts.id, posts.created_at, post_history.content, post_history.edited_at, users.username, users.status, avatars.file_path AS avatar_path, posts.thread_id, users.id AS user_id, post_history.original_markup, users.signature_rendered
|
||||||
|
FROM
|
||||||
|
thread_metadata tm
|
||||||
|
JOIN
|
||||||
|
posts ON posts.thread_id = tm.thread_id
|
||||||
|
JOIN
|
||||||
|
post_history ON posts.current_revision_id = post_history.id
|
||||||
|
JOIN
|
||||||
|
users ON posts.user_id = users.id
|
||||||
|
LEFT JOIN
|
||||||
|
threads ON threads.id = posts.thread_id
|
||||||
|
LEFT JOIN
|
||||||
|
avatars ON users.avatar_id = avatars.id
|
||||||
|
LEFT JOIN
|
||||||
|
subscriptions ON subscriptions.thread_id = posts.thread_id
|
||||||
|
WHERE
|
||||||
|
subscriptions.user_id = ? AND posts.created_at > subscriptions.last_seen
|
||||||
|
ORDER BY
|
||||||
|
tm.newest_post_time DESC, posts.created_at ASC"""
|
||||||
|
new_posts_raw = db.query(q, user.id, user.id)
|
||||||
|
current_thread_id = None
|
||||||
|
current_thread_group = None
|
||||||
|
total_unreads_count = 0
|
||||||
|
for row in new_posts_raw:
|
||||||
|
if row['thread_id'] != current_thread_id:
|
||||||
|
current_thread_group = {
|
||||||
|
'thread_id': row['thread_id'],
|
||||||
|
'thread_title': row['thread_title'],
|
||||||
|
'unread_count': row['unread_count'],
|
||||||
|
'thread_slug': row['thread_slug'],
|
||||||
|
'newest_post_time': row['newest_post_time'],
|
||||||
|
'posts': [],
|
||||||
|
}
|
||||||
|
total_unreads_count += int(row['unread_count'])
|
||||||
|
new_posts.append(current_thread_group)
|
||||||
|
current_thread_id = row['thread_id']
|
||||||
|
current_thread_group['posts'].append({
|
||||||
|
'id': row['id'],
|
||||||
|
'created_at': row['created_at'],
|
||||||
|
'content': row['content'],
|
||||||
|
'edited_at': row['edited_at'],
|
||||||
|
'username': row['username'],
|
||||||
|
'status': row['status'],
|
||||||
|
'avatar_path': row['avatar_path'],
|
||||||
|
'thread_id': row['thread_id'],
|
||||||
|
'user_id': row['user_id'],
|
||||||
|
'original_markup': row['original_markup'],
|
||||||
|
'signature_rendered': row['signature_rendered']
|
||||||
|
})
|
||||||
|
|
||||||
|
return render_template("users/inbox.html", new_posts = new_posts, total_unreads_count = total_unreads_count, all_subscriptions = all_subscriptions)
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
</form>
|
</form>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro full_post(post, render_sig = True, is_latest = False, editing = False, active_user = None) %}
|
{% macro full_post(post, render_sig = True, is_latest = False, editing = False, active_user = None, no_reply = false) %}
|
||||||
{% set postclass = "post" %}
|
{% set postclass = "post" %}
|
||||||
{% if editing %}
|
{% if editing %}
|
||||||
{% set postclass = postclass + " editing" %}
|
{% set postclass = postclass + " editing" %}
|
||||||
|
@ -20,6 +20,11 @@
|
|||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
{% if can_subscribe %}
|
{% if can_subscribe %}
|
||||||
|
<form class="modform" action="{{ url_for('threads.subscribe', slug=thread.slug) }}" method="post">
|
||||||
|
<input type='hidden' name='last_visible_post' value='{{posts[-1].id}}'>
|
||||||
|
<input type='hidden' name='subscribe' value='{{ 'unsubscribe' if is_subscribed else 'subscribe' }}'>
|
||||||
|
<input type='submit' value='{{ 'Unsubscribe' if is_subscribed else 'Subscribe' }}'>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_lock %}
|
{% if can_lock %}
|
||||||
<form class="modform" action="{{ url_for("threads.lock", slug=thread.slug) }}" method="post">
|
<form class="modform" action="{{ url_for("threads.lock", slug=thread.slug) }}" method="post">
|
||||||
|
51
app/templates/users/inbox.html
Normal file
51
app/templates/users/inbox.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{% from "common/macros.html" import timestamp, full_post %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}inbox{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="inbox-container">
|
||||||
|
{% if all_subscriptions is none %}
|
||||||
|
You have no subscriptions.<br>
|
||||||
|
{% else %}
|
||||||
|
Your subscriptions:
|
||||||
|
<ul>
|
||||||
|
{% for sub in all_subscriptions %}
|
||||||
|
<li>
|
||||||
|
<a href=" {{ url_for("threads.thread", slug=sub.thread_slug) }} ">{{ sub.thread_title }}</a>
|
||||||
|
<form class="modform" method="post" action="{{ url_for("threads.subscribe", slug = sub.thread_slug) }}">
|
||||||
|
<input type="hidden" name="subscribe" value="unsubscribe">
|
||||||
|
<input class="warn" type="submit" value="Unsubscribe">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% if not new_posts %}
|
||||||
|
You have no unread posts.
|
||||||
|
{% else %}
|
||||||
|
You have {{ total_unreads_count }} unread post{{(total_unreads_count | int) | pluralize }}:
|
||||||
|
{% for thread in new_posts %}
|
||||||
|
<div class="accordion">
|
||||||
|
<div class="accordion-header">
|
||||||
|
<button type="button" class="accordion-toggle">▼</button>
|
||||||
|
{% set latest_post_id = thread.posts[-1].id %}
|
||||||
|
{% set unread_posts_text = " (" + (thread.unread_count | string) + (" unread post" | pluralize) %}
|
||||||
|
<a class="accordion-title" href="{{ url_for("threads.thread", slug=latest_post_slug, after=latest_post_id, _anchor="post-" + (latest_post_id | string)) }}" title="Jump to latest post">{{thread.thread_title + unread_posts_text}}, latest at {{ timestamp(thread.newest_post_time) }})</a>
|
||||||
|
<form class="modform" method="post" action="{{ url_for("threads.subscribe", slug = thread.thread_slug) }}">
|
||||||
|
<input type="hidden" name="subscribe" value="read">
|
||||||
|
<input type="submit" value="Mark thread as Read">
|
||||||
|
</form>
|
||||||
|
<form class="modform" method="post" action="{{ url_for("threads.subscribe", slug = thread.thread_slug) }}">
|
||||||
|
<input type="hidden" name="subscribe" value="unsubscribe">
|
||||||
|
<input class="warn" type="submit" value="Unsubscribe">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="accordion-content">
|
||||||
|
{% for post in thread.posts %}
|
||||||
|
{{ full_post(post, no_reply = true) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user