Compare commits

..

2 Commits

Author SHA1 Message Date
93e0b80128 live status and countdown timer 2018-09-30 15:20:42 -04:00
eb4ccddb37 adjust header and chatPane height 2018-09-30 01:30:50 -04:00
13 changed files with 127 additions and 30 deletions

View File

@ -14,14 +14,14 @@
</head> </head>
<body> <body>
<div id="globalWrapper"> <div id="globalWrapper">
<ul id="header" class="header" style="{% if request.session.get("hide_header") == True %}display:none;{% else %}display:initial;{% endif %}"> <div id="header" class="header" style="{% if request.session.get("hide_header") == True %}display:none;{% else %}display:flex;{% endif %}">
<li><a onclick="toggle_header();" href="javascript:void(0);"></a></li> <span><a onclick="toggle_header();" href="javascript:void(0);"></a></span>
<li><a href="{{ url('homepage:index') }}">Home</a></li> <span><a href="{{ url('homepage:index') }}">Home</a></span>
{% block header %}{% endblock %} {% block header %}{% endblock %}
</ul> </div>
<ul id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:initial;{% else %}display:none;{% endif %}"> <div id="headerHidden" class="header" style="{% if request.session.get("hide_header") == True %}display:initial;{% else %}display:none;{% endif %}">
<li><a onclick="toggle_header();" href="javascript:void(0);"></a></li> <span><a onclick="toggle_header();" href="javascript:void(0);"></a></span>
</ul> </div>
<ul id="alerts"> <ul id="alerts">
{% for message in get_messages(request) %} {% for message in get_messages(request) %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li> <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>

View File

@ -42,3 +42,7 @@ class EditQuestForm(forms.Form):
Form for the /edit_quest page. Form for the /edit_quest page.
""" """
anon_name = forms.CharField(max_length=20) anon_name = forms.CharField(max_length=20)
live = forms.BooleanField(required=False)
live_date = forms.DateField(required=False)
live_time = forms.TimeField(required=False)
timezone = forms.IntegerField()

View File

@ -1,5 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Edit {{ quest.title }}{% endblock %} {% block title %}Edit {{ quest.title }}{% endblock %}
{% block head %}
<script>
window.onload = function() { document.getElementById('timezone').value = new Date().getTimezoneOffset(); };
</script>
{% endblock %}
{% block content %} {% block content %}
<center><h1>{{ quest.title }}</h1></center> <center><h1>{{ quest.title }}</h1></center>
<form method="post" action="{{ url('quest:edit_quest', args=[quest_id]) }}"> <form method="post" action="{{ url('quest:edit_quest', args=[quest_id]) }}">
@ -19,13 +24,15 @@
</tr> </tr>
<tr> <tr>
<td>Live:</td> <td>Live:</td>
<td><input type="checkbox" name="live" value="off"></td> <td><input type="checkbox" name="live"{% if quest.live %} checked{% endif %}></td>
</tr> </tr>
<tr> <tr>
<td>Live In:</td> <td>Live time:</td>
<td>Placeholder</td> <td><input type="date" name="live_date" id="live_date" value="{{ localtime(quest.live_time).strftime('%Y-%m-%d') }}"></td>
<td><input type="time" name="live_time" id="live_time" step="60" value="{{ localtime(quest.live_time).strftime('%H:%M:%S') }}"></td>
</tr> </tr>
</table> </table>
<input type="hidden" name="timezone" id="timezone">
<input type="submit"> <input type="submit">
</form> </form>
{% endblock %} {% endblock %}

View File

@ -7,7 +7,7 @@
{% endif %} {% endif %}
<script> <script>
const quest_id = {{ quest.id }}; const quest_id = {{ quest.id }};
const page_num = {{ page_num }}; const page_num = '{{ page_num }}';
const SCRIPT_NAME = '{{ request.META["SCRIPT_NAME"] }}'; const SCRIPT_NAME = '{{ request.META["SCRIPT_NAME"] }}';
const anon_name = '{{ quest.anon_name }}'; const anon_name = '{{ quest.anon_name }}';
</script> </script>
@ -19,9 +19,9 @@
{% endblock %} {% endblock %}
{% block header %} {% block header %}
{% if request.user == quest.owner %} {% if request.user == quest.owner %}
<li><a href="{{ url('quest:edit_quest', args=[quest_id]) }}">Edit Quest</a></li> <span><a href="{{ url('quest:edit_quest', args=[quest_id, page_num]) }}">Edit Quest</a></span>
{% endif %} {% endif %}
<li> <span>
<select onChange="window.location.href=this.value"> <select onChange="window.location.href=this.value">
<optgroup label="Pages"> <optgroup label="Pages">
{% for page in pages %} {% for page in pages %}
@ -36,8 +36,19 @@
</optgroup> </optgroup>
{% endif %} {% endif %}
</select> </select>
</li> </span>
<li id="toggleChat"><a onclick="toggle_chat()" href="javascript:void(0);">{% if request.session.get("hide_chat") == True %}←{% else %}→{% endif %}</a></li> {% if quest.live %}
<span id="live">
LIVE
</span>
{% else %}
{% if quest.live_time %}
<span id="liveIn">
Live in: <span id="liveCountdown"></span> (<span id="liveTime">{{ localtime(quest.live_time).strftime('%Y-%m-%d %H:%M') }}</span>)
</span>
{% endif %}
{% endif %}
<span id="toggleChat"><a onclick="toggle_chat()" href="javascript:void(0);">{% if request.session.get("hide_chat") == True %}←{% else %}→{% endif %}</a></span>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div id="questPane" style="width:{% if request.session.get("hide_chat") == True %}100%{% else %}70%{% endif %};"> <div id="questPane" style="width:{% if request.session.get("hide_chat") == True %}100%{% else %}70%{% endif %};">

View File

@ -0,0 +1,29 @@
# Generated by Django 2.1.1 on 2018-09-28 11:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('quest', '0002_auto_20180925_0936'),
]
operations = [
migrations.AddField(
model_name='quest',
name='live',
field=models.BooleanField(default=False),
preserve_default=False,
),
migrations.AddField(
model_name='quest',
name='live_time',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='page',
name='appendix',
field=models.BooleanField(),
),
]

View File

@ -15,6 +15,8 @@ class Quest(models.Model):
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE) on_delete=models.CASCADE)
anon_name = models.CharField(max_length=20, default="Anonymous") anon_name = models.CharField(max_length=20, default="Anonymous")
live = models.BooleanField()
live_time = models.DateTimeField(blank=True, null=True)
class Message(models.Model): class Message(models.Model):
@ -38,7 +40,7 @@ class Page(models.Model):
quest = models.ForeignKey(Quest, on_delete=models.CASCADE) quest = models.ForeignKey(Quest, on_delete=models.CASCADE)
page_num = models.CharField(max_length=4) page_num = models.CharField(max_length=4)
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
appendix = models.BooleanField(default=False) appendix = models.BooleanField()
def __str__(self): def __str__(self):
return self.title return self.title

View File

@ -8,6 +8,16 @@ h3 {
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
#live {
color: white;
background-color: red;
padding: 0.25em;
}
#toggleChat {
margin-left: auto;
}
#questPane { #questPane {
float: left; float: left;
box-sizing: border-box; box-sizing: border-box;
@ -111,8 +121,8 @@ h3 {
} }
#chatPane { #chatPane {
height: calc(100% - var(--header-height));
width: 30%; width: 30%;
height: calc(100vh - 2em);
right: 0; right: 0;
position: fixed; position: fixed;
display: flex; display: flex;

View File

@ -44,6 +44,7 @@ function load() {
socket.send('message', {message: text}); socket.send('message', {message: text});
} }
}); });
live_countdown();
} }
/* Websocket receive */ /* Websocket receive */
@ -265,3 +266,16 @@ function toggle_chat() {
toggle_cookie('hide_chat', 'off'); toggle_cookie('hide_chat', 'off');
} }
} }
function live_countdown() {
if (!document.getElementById('liveTime')) { return; }
setInterval(function() {
let delta = new Date(document.getElementById('liveTime').innerText) - new Date();
let days = parseInt(delta / (24*60*60*1000));
let hours = parseInt((delta % (24*60*60*1000)) / (60*60*1000));
let minutes = parseInt((delta % (60*60*1000)) / (60*1000));
let seconds = parseInt((delta % (60*1000)) / (1000));
let str = ((days) ? padToTwo(days) + ':' : '') + ((hours) ? padToTwo(hours) + ':' : '') + ((minutes) ? padToTwo(minutes) + ':' : '') + padToTwo(seconds);
document.getElementById('liveCountdown').innerText = str;
}, 1000);
}

View File

@ -9,8 +9,8 @@ from . import views
app_name = 'quest' app_name = 'quest'
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path('', views.index, name='index'),
path('<int:quest_id>', views.quest, name='quest'),
path('<int:quest_id>/<page_num>', views.quest, name='quest'),
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>/<page_num>', views.quest, name='quest'),
] ]

View File

@ -2,6 +2,8 @@
""" """
Quest and quest accessory views. Quest and quest accessory views.
""" """
from datetime import timedelta, datetime, timezone
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
@ -53,6 +55,21 @@ def edit_quest(request, quest_id, page_num=1):
form = EditQuestForm(request.POST) form = EditQuestForm(request.POST)
if form.is_valid(): if form.is_valid():
quest.anon_name = form.cleaned_data['anon_name'] quest.anon_name = form.cleaned_data['anon_name']
quest.live = form.cleaned_data['live']
live_date = form.cleaned_data['live_date']
live_time = form.cleaned_data['live_time']
if live_date and live_time:
live_datetime = datetime.combine(
live_date,
live_time,
timezone.utc
)
tz_delta = timedelta(minutes=form.cleaned_data['timezone'])
live_datetime = live_datetime + tz_delta
quest.live_time = live_datetime
else:
quest.live_time = None
quest.save() quest.save()
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:

View File

@ -1,7 +1,3 @@
:root {
--header-height: 1.25em;
}
body { body {
margin: 0; margin: 0;
} }
@ -18,13 +14,21 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
width: 100%; width: 100%;
height: var(--header-height); height: 2em;
list-style-type: none; list-style-type: none;
background-color: #dddddd; background-color: #dddddd;
display: flex;
align-items: center;
} }
.header li { .header span {
display: inline; padding-right: 0.25em;
padding-left: 0.25em;
}
.header span span {
padding-right: 0;
padding-left: 0;
} }
.header a { .header a {

View File

@ -6,13 +6,13 @@ function toggle_cookie(cookie, state) {
xhr.send(cookie + '=' + state); xhr.send(cookie + '=' + state);
} }
function toggle_header() { function toggle_header() {
if (document.getElementById('header').style.display == 'initial') { if (document.getElementById('header').style.display == 'flex') {
document.getElementById('header').style.display = 'none'; document.getElementById('header').style.display = 'none';
document.getElementById('headerHidden').style.display = 'initial'; document.getElementById('headerHidden').style.display = 'initial';
toggle_cookie('hide_header', 'on'); toggle_cookie('hide_header', 'on');
} }
else { else {
document.getElementById('header').style.display = 'initial'; document.getElementById('header').style.display = 'flex';
document.getElementById('headerHidden').style.display = 'none'; document.getElementById('headerHidden').style.display = 'none';
toggle_cookie('hide_header', 'off'); toggle_cookie('hide_header', 'off');
} }

3
todo
View File

@ -1,5 +1,4 @@
New Features: New Features:
Live indicator/countdown
Notifications Notifications
Banner images Banner images
Search page Search page
@ -10,13 +9,13 @@ Account managament/logout
Display profile link in header bar Display profile link in header bar
Tagging system Tagging system
Quote backlinks Quote backlinks
Quest homepage
Improvements: Improvements:
More options for text posts (lists and so on) More options for text posts (lists and so on)
More rigorous input checking in events.py More rigorous input checking in events.py
New post displays chat message New post displays chat message
Poll vote highlights entire option Poll vote highlights entire option
Poll vote doesn't disappear checkbox
Total voters per poll Total voters per poll
Chat archives Chat archives
Only last 100 (50?) chat messages are loaded on page load Only last 100 (50?) chat messages are loaded on page load