Adding notifications and +1's to messages

This commit is contained in:
Yusur 2019-11-25 09:39:33 +01:00
parent 29cf1532f7
commit baed59ea39
6 changed files with 168 additions and 8 deletions

View file

@ -2,10 +2,13 @@
## 0.9-dev
* Added positive feedback mechanism: now you can +1 a message. So, `score_message_add` and `score_message_remove` API endpoints were added, and `MessageUpvote` table was created.
* Added notifications support for API.
* Added `create_account` endpoint to API. This endpoint does not require an access token.
* Added `explore`, `notifications_count`, `notifications` and `notifications_seen` endpoints.
* Added `has_more` field to feed endpoints (`feed`, `explore` and `profile_feed`).
* Added `join_date` field into `user` object of `profile_info` endpoint, for more profile transparency.
* Added `/favicon.ico`.
* Added `explore` endpoint.
* Fixed some bugs when creating mentions and using offsets in feeds.
## 0.8.0

View file

@ -5,8 +5,9 @@ Warning: this is not the public API.
'''
from flask import Blueprint, jsonify
from .models import User
from .models import User, Message, MessageUpvote
from .utils import locations, get_current_user, is_username
import datetime
bp = Blueprint('ajax', __name__, url_prefix='/ajax')
@ -35,3 +36,30 @@ def location_search(name):
if value.lower().startswith(name.lower()):
results.append({'value': key, 'display': value})
return jsonify({'results': results})
@bp.route('/score/<int:id>/toggle', methods=['POST'])
def score_toggle(id):
user = get_current_user()
message = Message[id]
upvoted_by_self = (MessageUpvote
.select()
.where((MessageUpvote.message == message) & (MessageUpvote.user == user))
.exists())
if upvoted_by_self:
(MessageUpvote
.delete()
.where(
(MessageUpvote.message == message) &
(MessageUpvote.user == user))
.execute()
)
else:
MessageUpvote.create(
message=message,
user=user,
created_date=datetime.datetime.now()
)
return jsonify({
"score": message.score,
"status": "ok"
})

View file

@ -2,7 +2,8 @@ from flask import Blueprint, jsonify, request
import sys, os, datetime, re, uuid
from functools import wraps
from peewee import IntegrityError
from .models import User, UserProfile, Message, Upload, Relationship, database, \
from .models import User, UserProfile, Message, Upload, Relationship, Notification, \
MessageUpvote, database, \
MSGPRV_PUBLIC, MSGPRV_UNLISTED, MSGPRV_FRIENDS, MSGPRV_ONLYME, UPLOAD_DIRECTORY
from .utils import check_access_token, Visibility, push_notification, unpush_notification, \
create_mentions, is_username, generate_access_token, pwdhash
@ -25,7 +26,9 @@ def get_message_info(message):
'text': message.text,
'privacy': message.privacy,
'pub_date': message.pub_date.timestamp(),
'media': media
'media': media,
'score': len(message.upvotes),
'upvoted_by_self': message.upvoted_by_self(),
}
def validate_access(func):
@ -162,6 +165,7 @@ def profile_info(self, userid):
"generation": profile.year,
"instagram": profile.instagram,
"facebook": profile.facebook,
"join_date": user.join_date.timestamp(),
"relationships": get_relationship_info(self, user),
"messages_count": len(user.messages),
"followers_count": len(user.followers()),
@ -348,3 +352,82 @@ def create_account():
return jsonify({'access_token': generate_access_token(user), 'status': 'ok'})
except Exception as e:
return jsonify({'message': str(e), 'status': 'fail'})
def get_notification_info(notification):
obj = {
"id": notification.id,
"type": notification.type,
"timestamp": notification.pub_date.timestamp(),
"seen": notification.seen
}
obj.update(json.loads(notification.detail))
return obj
@bp.route('/notifications/count')
@validate_access
def notifications_count(self):
count = len(Notification
.select()
.where((Notification.target == self) & (Notification.seen == 0)))
return {
'count': count
}
@bp.route('/notifications')
@validate_access
def notifications(self):
items = []
query = (Notification
.select()
.where(Notification.target == self)
.order_by(Notification.pub_date.desc())
.limit(100))
unseen_count = len(Notification
.select()
.where((Notification.target == self) & (Notification.seen == 0)))
for notification in query:
items.append(get_notification_info(query))
return {
"notifications": {
"items": items,
"unseen_count": unseen_count
}
}
@bp.route('/notifications/seen', methods=['POST'])
@validate_access
def notifications_seen(self):
data = request.get_json(True)
(Notification
.update(seen=1)
.where((Notification.target == self) & (Notification.pub_date < data['offset']))
.execute())
return {}
@bp.route('/score/message/<int:id>/add', methods=['POST'])
@validate_access
def score_message_add(self, id):
message = Message[id]
MessageUpvote.create(
message=message,
user=self,
created_date=datetime.datetime.now()
)
return {
'score': len(message.upvotes)
}
@bp.route('/score/message/<int:id>/remove', methods=['POST'])
@validate_access
def score_message_remove(self, id):
message = Message[id]
(MessageUpvote
.delete()
.where(
(MessageUpvote.message == message) &
(MessageUpvote.user == self))
.execute()
)
return {
'score': len(message.upvotes)
}

View file

@ -162,6 +162,17 @@ class Message(BaseModel):
return user.is_following(cur_user) and cur_user.is_following(user)
else:
return False
@property
def score(self):
return self.upvotes.count()
def upvoted_by_self(self):
from .utils import get_current_user
user = get_current_user()
return (MessageUpvote
.select()
.where((MessageUpvote.message == self) & (MessageUpvote.user == user))
.exists()
)
# this model contains two foreign keys to user -- it essentially allows us to
# model a "many-to-many" relationship between users. by querying and joining
@ -226,6 +237,8 @@ report_reasons = [
(REPORT_REASON_FIREARMS, "Sale or promotion of firearms"),
(REPORT_REASON_DRUGS, "Sale or promotion of drugs"),
(REPORT_REASON_UNDERAGE, "This user is less than 13 years old"),
(REPORT_REASON_LEAK, "Leak of sensitive information"),
(REPORT_REASON_DMCA, "Copyright violation")
]
REPORT_STATUS_DELIVERED = 0
@ -251,10 +264,21 @@ class Report(BaseModel):
except DoesNotExist:
return
# New in 0.9.
class MessageUpvote(BaseModel):
message = ForeignKeyField(Message, backref='upvotes')
user = ForeignKeyField(User)
created_date = DateTimeField()
class Meta:
indexes = (
(('message', 'user'), True),
)
def create_tables():
with database:
database.create_tables([
User, UserAdminship, UserProfile, Message, Relationship,
Upload, Notification, Report])
Upload, Notification, Report, MessageUpvote])
if not os.path.isdir(UPLOAD_DIRECTORY):
os.makedirs(UPLOAD_DIRECTORY)

View file

@ -98,3 +98,21 @@ function showHideMessageOptions(id){
options.style.display = 'block';
}
}
function toggleUpvote(id){
var msgElem = document.getElementById(id);
var upvoteLink = msgElem.getElementsByClassName('message-upvote')[0];
var scoreCounter = msgElem.getElementsByClassName('message-score')[0];
var xhr = new XMLHttpRequest();
xhr.open("POST", "/ajax/score/" + id + "/toggle", true);
xhr.onreadystatechange = function(){
if(xhr.readyState == XMLHttpRequest.DONE){
if(xhr.status == 200){
console.log('liked #' + id);
var data = JSON.parse(xhr.responseText);
scoreCounter.innerHTML = data.score;
}
}
};
xhr.send();
}

View file

@ -5,12 +5,16 @@
</div>
{% endif %}
<p class="message-footer">
<a href="javascript:void(0);" class="message-upvote" onclick="toggleUpvote({{ message.id }});">+</a>
<span class="message-score">{{ message.score }}</span>
-
<a href="{{ url_for('website.user_detail', username=message.user.username) }}">{{ message.user.username }}</a>
-
{% set message_privacy = message.privacy %}
{% if message.privacy in (0, 1) %} Public
{% elif message.privacy == 2 %} Friends
{% elif message.privacy == 3 %} Only me
{% if message_privacy == 0 %} Public
{% elif message_privacy == 1 %} Unlisted
{% elif message_privacy == 2 %} Friends
{% elif message_privacy == 3 %} Only me
{% endif %}
-
<time datetime="{{ message.pub_date.isoformat() }}" title="{{ message.pub_date.ctime() }}">{{ message.pub_date | human_date }}</time>