Skip to content
Snippets Groups Projects
Commit 792f070d authored by Jiří Setnička's avatar Jiří Setnička
Browse files

Zprávičky: Zobrazení a pravidelný reload v účastnické části

Reloaduje se jednou za 30s z JSON endpointu, při změně počtu zpráv začne blikat
záhlaví okna a nová zprávička se zvýrazní.
parent 6c24f402
No related branches found
No related tags found
1 merge request!57Zprávičky
......@@ -114,6 +114,7 @@ mo.ext.assets.Assets(app, url_prefix='/assets', asset_dir=static_dir)
app.assets.add_assets([
'bootstrap.min.css',
'mo.css',
'js/news-reloader.js',
])
......
......
<div id="novinky">
{% include "parts/messages.html" %}
</div>
<script type="text/javascript">
r = new NewsReloader(document.getElementById("novinky"), "{{ url }}", 60000);
</script>
......@@ -3,6 +3,12 @@
{% set round = contest.round %}
{% set state = contest.ct_state() %}
{% block head %}
{% if contest.round.has_messages %}
<script src="{{ asset_url('js/news-reloader.js') }}" type="text/javascript"></script>
{% endif %}
{% endblock %}
{% block title %}{{ round.name }} {{ round.year }}. ročníku kategorie {{ round.category }}: {{ contest.place.name }}{% endblock %}
{% block breadcrumbs %}
<li><a href='{{ url_for('user_index') }}'>Soutěže</a>
......@@ -98,4 +104,12 @@
</table>
{% endif %}
{% if contest.round.has_messages %}
<h3>Novinky k soutěži</h3>
{% with title="Novinky k soutěži", url=url_for('user_contest_news', id=contest.contest_id) %}
{% include "parts/user_news.html" %}
{% endwith %}
{% endif %}
{% endblock %}
......@@ -3,6 +3,12 @@
{% set round = contest.round %}
{% set state = contest.ct_state() %}
{% block head %}
{% if contest.round.has_messages %}
<script src="{{ asset_url('js/news-reloader.js') }}" type="text/javascript"></script>
{% endif %}
{% endblock %}
{% block title %}Úloha {{ task.code }}: {{ task.name }}{% endblock %}
{% block breadcrumbs %}
<li><a href='{{ url_for('user_index') }}'>Soutěže</a>
......@@ -85,4 +91,11 @@
{% endif %}
{% endif %}
{% if contest.round.has_messages %}
<h3>Novinky k soutěži</h3>
{% with title="Novinky k soutěži", url=url_for('user_contest_news', id=contest.contest_id) %}
{% include "parts/user_news.html" %}
{% endwith %}
{% endif %}
{% endblock %}
from flask import render_template, request, g, redirect, url_for, flash
from flask import render_template, jsonify, g, redirect, url_for, flash
from flask_wtf import FlaskForm
import flask_wtf.file
from sqlalchemy import and_
from sqlalchemy.orm import joinedload
import werkzeug.exceptions
import wtforms
import wtforms.validators as validators
import mo.config as config
import mo.db as db
import mo.submit
import mo.util
from mo.util import logger
from mo.util_format import time_and_timedelta
from mo.web import app
import mo.web.util
......@@ -66,9 +66,12 @@ def get_task(contest: db.Contest, id: int) -> db.Task:
@app.route('/user/contest/<int:id>/')
def user_contest(id: int):
sess = db.get_session()
contest = get_contest(id)
task_sols = (db.get_session().query(db.Task, db.Solution)
messages = sess.query(db.Message).filter_by(round_id=contest.round_id).order_by(db.Message.created_at).all()
task_sols = (sess.query(db.Task, db.Solution)
.select_from(db.Task)
.outerjoin(db.Solution, and_(db.Solution.task_id == db.Task.task_id, db.Solution.user == g.user))
.filter(db.Task.round == contest.round)
......@@ -81,10 +84,27 @@ def user_contest(id: int):
'user_contest.html',
contest=contest,
task_sols=task_sols,
messages=messages,
max_submit_size=config.MAX_CONTENT_LENGTH,
)
@app.route('/user/contest/<int:id>/news')
def user_contest_news(id: int):
sess = db.get_session()
contest = get_contest(id)
messages = sess.query(db.Message).filter_by(round_id=contest.round_id).order_by(db.Message.created_at).all()
out_messages = [{
'title': msg.title,
'date_format': time_and_timedelta(msg.created_at),
'body': msg.html,
} for msg in messages]
return jsonify(out_messages)
@app.route('/user/contest/<int:id>/task-statement/zadani.pdf')
def user_task_statement(id: int):
contest = get_contest(id)
......@@ -108,6 +128,8 @@ def user_contest_task(contest_id: int, task_id: int):
task = get_task(contest, task_id)
sess = db.get_session()
messages = sess.query(db.Message).filter_by(round_id=contest.round_id).order_by(db.Message.created_at).all()
state = contest.ct_state()
if state == db.RoundState.preparing:
# Dokud se kolo připravuje nebo čeká na zveřejnění zadání, tak ani nezobrazujeme
......@@ -164,6 +186,7 @@ def user_contest_task(contest_id: int, task_id: int):
sol=sol,
papers=papers,
form=form,
messages=messages,
)
......
......
class NewsReloader {
news_count = 0;
notification_interval = null;
original_title = "";
constructor(element, url, check_interval=60000) {
this.element = element;
this.url = url;
this.check_interval = check_interval;
this.news_count = element.childElementCount;
this.original_title = document.title;
var t = this
setInterval(function() { t.refreshNews();}, this.check_interval);
window.addEventListener('focus', function() {
t.news_count = t.element.childElementCount;
t.notificationOff();
});
}
notificationOn(notification) {
clearInterval(this.notification_interval); // clear any previous interval
var t = this;
this.notification_interval = setInterval(function() {
document.title = (t.original_title == document.title)
? notification + t.original_title
: t.original_title;
}, 1000);
}
notificationOff() {
if (this.notification_interval) {
clearInterval(this.notification_interval);
document.title = this.original_title;
}
}
refreshNews() {
var xmlhttp = new XMLHttpRequest();
var t = this;
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var newsArr = JSON.parse(this.responseText);
var count = newsArr.length
var markN = 0; // how many elements to mark with class "new"
if (count > t.news_count) {
markN = count - t.news_count;
}
// Create all new <div>s
var newElements = document.createDocumentFragment();
for (var i = 0; i < count; i++) {
var div = document.createElement("div");
div.className = "message";
if (i + markN >= count) div.className += " new"; // mark N last elements
div.innerHTML = "<span class='msg-title'>" + newsArr[i]["title"] + "</span>"
+"<span class='msg-date'>" + newsArr[i]["date_format"] + "</span>"
+"<div class='msg-text'>" + newsArr[i]["body"] + "</div>";
newElements.appendChild(div);
}
// Remove all childs and append new
t.element.innerHTML = "";
t.element.appendChild(newElements);
// Notification
if (count != t.news_count) {
if (document.hasFocus()) t.news_count = count;
else {
// Add (1) to the title (with removing any previous value)
t.notificationOn("(" + Math.abs(count - t.news_count).toString() + ") ");
}
}
}
}
xmlhttp.open("GET", this.url, true);
xmlhttp.send();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment