Compare commits

...

2 Commits

29 changed files with 159 additions and 74 deletions

5
api/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'

16
api/urls.py Normal file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
"""
API URL configuration.
"""
from django.urls import path
from . import views
app_name = 'api'
urlpatterns = [
path('', views.index, name='index'),
path('set_session', views.set_session, name='set_session'),
path('subscribe', views.subscribe, name='subscribe'),
path('clear_notification', views.clear_notification, name='clear_notification'),
]

60
api/views.py Normal file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
API endpoints.
"""
from user_messages import api
from django.http import HttpResponse
from django.shortcuts import redirect
from django.views.decorators.http import require_POST
from quest.models import Quest
def index(request):
"""An index page."""
return HttpResponse('Hello.')
@require_POST
def set_session(request):
"""
API endpoint for setting certain values in the users session.
"""
for key, value in request.POST.items():
if key not in ['hide_header', 'hide_chat']:
continue
if value == 'on':
request.session[key] = True
elif value == 'off':
request.session[key] = False
return HttpResponse('true')
@require_POST
def subscribe(request):
"""Endpoint for users to subscribe to a quest."""
if not request.user.is_authenticated:
return redirect('login:index')
quest_id = request.POST.get('quest_id')
try:
quest = Quest.objects.get(id=quest_id)
except Quest.DoesNotExist:
return redirect('quest:quest', quest_id=quest_id, page_num='0')
request.user.subscriptions.add(quest)
return redirect('quest:quest', quest_id=quest.id, page_num='0')
@require_POST
def clear_notification(request):
"""Endpoint for users to clear a message notification."""
if not request.user.is_authenticated:
return redirect('login:index')
msg_id = request.POST.get('msg_id')
messages = api.get_messages(user=request.user)
message = [m for m in messages if str(m.id) == msg_id]
if message:
message = message[0]
else:
return HttpResponse('false')
message.delete()
return HttpResponse('true')

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="{{ static('base.css') }}">
<script type="text/javascript" src="{{ static('base.js') }}"></script>
<script>
const set_session_url = '{{ url("set_session:index") }}';
const api_url = '{{ url("api:index") }}';
const csrf_token = '{{ csrf_token }}';
</script>
{% block head %}{% endblock %}
@ -17,6 +17,21 @@
<div id="header" class="header" style="{% if request.session.get("hide_header") == True %}display:none;{% else %}display:flex;{% endif %}">
<span><a onclick="toggle_header();" href="javascript:void(0);"></a></span>
<span><a href="{{ url('homepage:index') }}">Home</a></span>
{% if request.user.is_authenticated %}
<span id="profileLink"><a href="{{ url('user:profile', args=[request.user.id]) }}">{{ request.user.username }}</a></span>
<span id="subscriptions">
<button id="subbtn">{{ get_sub_msgs(request=request)|length }}</button>
<div id="subscriptionsContent">
{% for sub in get_sub_msgs(request=request) %}
<div id="notification-{{ sub.id }}" >
{{ sub.created_at.strftime('%Y-%m-%d') }} <a href="{{ sub.meta.url }}">{{ sub }}</a>
<a href="javascript:void(0);" onclick="clear_notification({{ sub.id }})">X</a>
<br>
</div>
{% endfor %}
</div>
</span>
{% endif %}
{% block header %}{% endblock %}
</div>
<div id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:flex;{% else %}display:none;{% endif %}">

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -24,8 +24,5 @@
Live in: <span id="liveCountdown"></span> (<span id="liveTime">{% if quest.live_time %}{{ localtime(quest.live_time).strftime('%Y-%m-%d %H:%M') }}{% endif %}</span>)
</span>
<span id="space"></span>
{% if request.user.is_authenticated %}
<span id="profileLink"><a href="{{ url('user:profile', args=[request.user.id]) }}">{{ request.user.username }}</a></span>
{% endif %}
<span id="toggleChat"><a onclick="toggle_chat()" href="javascript:void(0);">{% if request.session.get("hide_chat") == True %}←{% else %}→{% endif %}</a></span>

View File

@ -38,9 +38,10 @@
</form>
{% endif %}
{% if request.user.is_authenticated %}
<form id="subscribe" method="post" action="{{ url('quest:subscribe', kwargs={'quest_id': quest_id}) }}">
<form id="subscribe" method="post" action="{{ url('api:subscribe') }}">
{{ csrf_input }}
<a onclick="document.getElementById('subscribe').submit()">Subscribe</a>
<input type="hidden" name="quest_id" value="{{ quest_id }}">
<a href="javascript:void(0);" onclick="document.getElementById('subscribe').submit()">Subscribe</a>
</form>
{% endif %}
<h3>Pages</h3>

View File

@ -10,7 +10,6 @@ app_name = 'quest'
urlpatterns = [
path('', views.index, name='index'),
path('<int:quest_id>/new_tag', views.new_tag, name='new_tag'),
path('<int:quest_id>/subscribe', views.subscribe, name='subscribe'),
path('<int:quest_id>/edit_quest', views.edit_quest, name='edit_quest'),
path('<int:quest_id>/<page_num>/edit_quest', views.edit_quest, name='edit_quest'),
path('<int:quest_id>', views.quest, name='quest'),

View File

@ -11,6 +11,8 @@ from django.contrib import messages
from django.shortcuts import render, redirect
from django.db.models import F
from django.conf import settings
from django.urls import reverse
from django.http import HttpResponse
from .models import Quest, DiceRoll, PollOption, PollVote, Page, Post
from .forms import EditQuestForm, QuestForm, PostForm
@ -128,7 +130,13 @@ def edit_quest(request, quest_id, page_num='0'):
subscriber,
f"{quest.title} has gone live!",
extra_tags='subscription',
deliver_once=False
meta={
'url': reverse(
'quest:quest',
args=(quest.id, 0),
),
},
deliver_once=False,
)
return redirect('quest:quest',quest_id=quest.id, page_num=page_num)
else:
@ -187,13 +195,3 @@ def new_tag(request, quest_id):
quest.tags.add(tag)
return redirect('quest:quest', quest_id=quest_id, page_num='0')
@require_POST
def subscribe(request, quest_id):
"""Endpoint for users to subscribe to a quest."""
if not request.user.is_authenticated:
return redirect('login:index')
request.user.subscriptions.add(Quest.objects.get(id=quest_id))
messages.success(request, "Subscribed")
return redirect('quest:quest', quest_id=quest_id, page_num='0')

View File

@ -1,5 +0,0 @@
from django.apps import AppConfig
class SetSessionConfig(AppConfig):
name = 'set_session'

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -1,12 +0,0 @@
#!/usr/bin/env python3
"""
set_session app URL configuration.
"""
from django.urls import path
from . import views
app_name = 'set_session'
urlpatterns = [
path('', views.index, name='index'),
]

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
"""
/set_session app views.
"""
from django.http import HttpResponse
from django.views.decorators.http import require_POST
@require_POST
def index(request):
"""
A simple API endpoint for setting certain values in the users session.
"""
for key, value in request.POST.items():
if key not in ['hide_header', 'hide_chat']:
continue
if value == 'on':
request.session[key] = True
elif value == 'off':
request.session[key] = False
return HttpResponse('true')

View File

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View File

@ -55,6 +55,30 @@ a:hover {
padding-left: 0;
}
#subscriptions {
position: relative;
display: inline-block;
}
#subbtn {
width: 1.5em;
height: 1.5em;
background-color: orange;
border: none;
}
#subscriptionsContent {
display: none;
position: absolute;
background-color: #FAFAFA;
border: 1px solid #ccc;
min-width: 25em;
}
#subscriptions:hover #subscriptionsContent {
display: block;
}
#headerHidden {
width: 1em;
border-right: 1px solid #ccc;

View File

@ -1,10 +1,10 @@
function toggle_cookie(cookie, state) {
let xhr = new XMLHttpRequest();
xhr.open('POST', set_session_url, true);
xhr.open('POST', api_url + '/set_session', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRFToken', csrf_token);
xhr.send(cookie + '=' + state);
}
}
function toggle_header() {
if (document.getElementById('header').style.display == 'flex') {
document.getElementById('header').style.display = 'none';
@ -17,3 +17,15 @@ function toggle_header() {
toggle_cookie('hide_header', 'off');
}
}
function clear_notification(msg_id) {
let xhr = new XMLHttpRequest();
xhr.open('POST', api_url + '/clear_notification', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRFToken', csrf_token);
xhr.send('msg_id=' + msg_id);
let elem = document.getElementById('notification-' + msg_id);
elem.parentNode.removeChild(elem);
elem = document.getElementById('subbtn');
elem.innerText = elem.innerText - 1;
}

View File

@ -8,6 +8,15 @@ from django.urls import reverse
from django.utils.timezone import template_localtime
from django.contrib.staticfiles.storage import staticfiles_storage
def get_sub_msgs(**kwargs):
"""Returns all messages 'subscription' in the tag."""
messages = api.get_messages(**kwargs)
sub_msgs = []
for message in messages:
if 'subscription' in message.tags:
sub_msgs.insert(0, message)
return sub_msgs
def environment(**options):
"""
Custom Jinja2 environment.
@ -23,5 +32,6 @@ def environment(**options):
'static': staticfiles_storage.url,
'get_messages': api.get_messages,
'localtime': template_localtime,
'get_sub_msgs': get_sub_msgs,
})
return env

View File

@ -9,7 +9,7 @@ urlpatterns = [
path('', include('homepage.urls')),
path('admin/', admin.site.urls),
path('quest/', include('quest.urls')),
path('set_session/', include('set_session.urls')),
path('api/', include('api.urls')),
path('signup/', include('signup.urls')),
path('login/', include('login.urls')),
path('logout/', include('logout.urls')),

1
todo
View File

@ -14,3 +14,4 @@ Poll vote highlights entire option
Total voters per poll
Chat archives
Adjust quote preview postioning
Better API