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') }}"> <link rel="stylesheet" type="text/css" href="{{ static('base.css') }}">
<script type="text/javascript" src="{{ static('base.js') }}"></script> <script type="text/javascript" src="{{ static('base.js') }}"></script>
<script> <script>
const set_session_url = '{{ url("set_session:index") }}'; const api_url = '{{ url("api:index") }}';
const csrf_token = '{{ csrf_token }}'; const csrf_token = '{{ csrf_token }}';
</script> </script>
{% block head %}{% endblock %} {% 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 %}"> <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 onclick="toggle_header();" href="javascript:void(0);"></a></span>
<span><a href="{{ url('homepage:index') }}">Home</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 %} {% block header %}{% endblock %}
</div> </div>
<div id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:flex;{% else %}display:none;{% endif %}"> <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>) 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>
<span id="space"></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> <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> </form>
{% endif %} {% endif %}
{% if request.user.is_authenticated %} {% 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 }} {{ 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> </form>
{% endif %} {% endif %}
<h3>Pages</h3> <h3>Pages</h3>

View File

@ -10,7 +10,6 @@ app_name = 'quest'
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path('', views.index, name='index'),
path('<int:quest_id>/new_tag', views.new_tag, name='new_tag'), 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>/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>/<page_num>/edit_quest', views.edit_quest, name='edit_quest'),
path('<int:quest_id>', views.quest, name='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.shortcuts import render, redirect
from django.db.models import F from django.db.models import F
from django.conf import settings 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 .models import Quest, DiceRoll, PollOption, PollVote, Page, Post
from .forms import EditQuestForm, QuestForm, PostForm from .forms import EditQuestForm, QuestForm, PostForm
@ -128,7 +130,13 @@ def edit_quest(request, quest_id, page_num='0'):
subscriber, subscriber,
f"{quest.title} has gone live!", f"{quest.title} has gone live!",
extra_tags='subscription', 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) return redirect('quest:quest',quest_id=quest.id, page_num=page_num)
else: else:
@ -187,13 +195,3 @@ def new_tag(request, quest_id):
quest.tags.add(tag) quest.tags.add(tag)
return redirect('quest:quest', quest_id=quest_id, page_num='0') 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; 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 { #headerHidden {
width: 1em; width: 1em;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;

View File

@ -1,6 +1,6 @@
function toggle_cookie(cookie, state) { function toggle_cookie(cookie, state) {
let xhr = new XMLHttpRequest(); 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('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRFToken', csrf_token); xhr.setRequestHeader('X-CSRFToken', csrf_token);
xhr.send(cookie + '=' + state); xhr.send(cookie + '=' + state);
@ -17,3 +17,15 @@ function toggle_header() {
toggle_cookie('hide_header', 'off'); 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.utils.timezone import template_localtime
from django.contrib.staticfiles.storage import staticfiles_storage 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): def environment(**options):
""" """
Custom Jinja2 environment. Custom Jinja2 environment.
@ -23,5 +32,6 @@ def environment(**options):
'static': staticfiles_storage.url, 'static': staticfiles_storage.url,
'get_messages': api.get_messages, 'get_messages': api.get_messages,
'localtime': template_localtime, 'localtime': template_localtime,
'get_sub_msgs': get_sub_msgs,
}) })
return env return env

View File

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

1
todo
View File

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