From 6fc506df2f4679807a42687cf996ca8ca4f4588b Mon Sep 17 00:00:00 2001 From: j Date: Mon, 24 Jul 2023 22:00:43 +0100 Subject: [PATCH] login/register/post --- app/item/models.py | 9 ++ app/item/views.py | 8 +- app/static/css/comments.scss | 97 +++++++++------ app/static/js/comments.js | 235 ++++++++++++++++++++++++++++------- app/templates/item.html | 31 +++-- app/user/views.py | 11 +- 6 files changed, 283 insertions(+), 108 deletions(-) diff --git a/app/item/models.py b/app/item/models.py index 0ce73a2..9584121 100644 --- a/app/item/models.py +++ b/app/item/models.py @@ -8,6 +8,7 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.db import models from django.urls import reverse +from django.utils.timesince import timesince User = get_user_model() @@ -133,6 +134,12 @@ class Comment(models.Model): @property def date(self): + now = timezone.now() + difference = now - self.created + if difference <= timedelta(minutes=1): + return "just now" + return '%(time)s ago' % {'time': timesince(self.created).split(', ')[0]} + return self.created.strftime('%B %d, %Y at %H:%M') return self.created.strftime('%Y-%m-%d %H:%M') def json(self): @@ -143,5 +150,7 @@ class Comment(models.Model): data['name'] = self.name data['date'] = self.date data['text'] = self.text + data['id'] = self.id + data['published'] = self.is_published return data diff --git a/app/item/views.py b/app/item/views.py index b2df7f9..8c993a3 100644 --- a/app/item/views.py +++ b/app/item/views.py @@ -43,13 +43,7 @@ def item(request, id): qs = qs.filter(q) comments = [] for comment in qs: - comments.append({ - "id": comment.id, - "name": comment.name, - "date": comment.date, - "text": comment.text, - "published": comment.is_published, - }) + comments.append(comment.json()) context['comments'] = mark_safe(json.dumps(comments)) user = {} if request.user.is_staff: diff --git a/app/static/css/comments.scss b/app/static/css/comments.scss index d867f9d..586abac 100644 --- a/app/static/css/comments.scss +++ b/app/static/css/comments.scss @@ -22,47 +22,69 @@ padding-top: 8px; padding-left: 4px; + .buttons { + display: none; + &.active { + display: block; + } + } + input, button { + height: 36px; + } + input { + width: calc(50% - 8px); + } + .buttons { + &.login { + input { + width: calc(100% - 8px); + } + } + } + input.invalid, + textarea.invalid { + border-bottom: 1px solid purple; + } + + textarea, + input { + padding: 4px; + margin-left: 0; + margin-top: 4px; + margin-bottom: 4px; + margin-right: 4px; + background: none; + color: white; + border: 1px solid green; + } + textarea { + width: calc(100% - 8px); + height: 100px; + display: block; + } + button { + background: black; + color: white; + border: solid 1px green; + padding: 8px; + } + button:hover, + button:active { + border: solid 1px lightgreen; + cursor: pointer; + } } @media(max-width:768px) { .add-comment { padding-left: 4px; - } -} - -.add-comment textarea, -.add-comment input { - padding: 4px; - margin-left: 0; - margin-top: 4px; - margin-bottom: 4px; - margin-right: 4px; - background: none; - color: white; - border: 1px solid green; -} -.add-comment input { - width: calc(50% - 8px); -} -.add-comment textarea { - width: calc(100% - 8px); - height: 100px; - display: block; -} -.add-comment button { - background: black; - color: white; - border: solid 1px green; - padding: 8px; -} -.add-comment button:hover { - border: solid 1px lightgreen; - cursor: pointer; -} -.add-comment { - .buttons { - &.login { - input { - width: calc(25% - 8px); + input { + width: calc(100% - 8px); + } + .buttons { + &.login { + input { + width: calc(100% - 8px); + } } } } @@ -99,3 +121,4 @@ button.publish-comment { height: 16px; } } + diff --git a/app/static/js/comments.js b/app/static/js/comments.js index eef7a71..d1250b4 100644 --- a/app/static/js/comments.js +++ b/app/static/js/comments.js @@ -1,14 +1,42 @@ -async function publish_comment(id) { - var csrf +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie !== '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} + +function getCSRF() { + return document.querySelector('input[name="csrfmiddlewaretoken"]').value +} + +function serializeData(fields) { + var data = {}; document.querySelector('.add-comment').querySelectorAll('input,textarea').forEach(input => { - if (input.name == 'csrfmiddlewaretoken') { - csrf = input.value.trim() + if (fields && !fields.includes(input.name)) { + return + } + if (input.name != 'csrfmiddlewaretoken') { + var value = input.value.trim() + if (value) { + data[input.name] = value + } } }) - var data = { - "comment": id - } - return fetch("/comment/publish/", { + return data +} + +async function api(url, data) { + var csrf = getCSRF() + return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -19,30 +47,26 @@ async function publish_comment(id) { return response.json() }) } +async function publish_comment(id) { + return api("/comment/publish/", { + "comment": id + }) +} -async function login(username, password) { - var csrf - document.querySelector('.add-comment').querySelectorAll('input,textarea').forEach(input => { - if (input.name == 'csrfmiddlewaretoken') { - csrf = input.value.trim() - } - }) - var data = { - "username": username, - "password": password, - } - return fetch("/login/", { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrf - }, - body: JSON.stringify(data) - }).then(response => { - return response.json() +async function logout() { + return api("/logout/", {}).then(response => { + delete user.username }) } +async function login(data) { + return api("/login/", data) +} + +async function register(data) { + return api("/register/", data) +} + function renderComments(cdiv, data) { cdiv.innerHTML = `

@@ -70,7 +94,6 @@ function renderComments(cdiv, data) {
${comment.text}
by ${comment.name} - on ${comment.date} ${extra}
@@ -101,29 +124,31 @@ function renderComments(cdiv, data) { }) } + +function clearInvalid() { + document.querySelectorAll('input.invalid, textarea.invalid').forEach(invalid => { + invalid.classList.remove('invalid') + }) +} + document.querySelector('button#add-comment').addEventListener('click', event => { - var data = {}, csrf; - document.querySelector('.add-comment').querySelectorAll('input,textarea').forEach(input => { - if (input.name == 'csrfmiddlewaretoken') { - csrf = input.value.trim() - } else { - data[input.name] = input.value.trim() - if (!data[input.name]) { - delete data[input.name] - } + event.preventDefault() + event.stopPropagation() + var valid = true, fields = ['text', 'name', 'email'] + var element = document.querySelector('.add-comment .comment-fields') + element.querySelectorAll('input:invalid, textarea:invalid').forEach(invalid => { + if (fields.includes(invalid.name)) { + invalid.classList.add('invalid') + valid = false + console.log('invalid', invalid) } }) + if (!valid) { + return + } + var data = serializeData(fields); data.item = pandora.comment - fetch("/comment/", { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRFToken': csrf - }, - body: JSON.stringify(data) - }).then(response => { - return response.json() - }).then(response => { + api("/comment/", data).then(response => { var comment = document.createElement('div') comment.classList.add('comment') comment.innerHTML = ` @@ -139,5 +164,119 @@ document.querySelector('button#add-comment').addEventListener('click', event => comment.querySelector('.text').innerText = response.text document.querySelector('.comments .comments-content').append(comment) document.querySelector('.add-comment textarea').value = '' + if (!user.username) { + localStorage.name = data.name + localStorage.email = data.email + } }) }) + +function afterLogin() { + document.querySelector('input[name="csrfmiddlewaretoken"]').value = getCookie('csrftoken') + user.username = data.username + document.querySelectorAll('.add-comment input[name="email"]').forEach(input => { + input.required = false + }) + document.querySelectorAll('.add-comment input[type="password"]').forEach(input => { + input.value = "" + input.required = false + }) + document.querySelector('.add-comment .buttons.login').classList.remove('active') + var buttons = document.querySelector('.add-comment .buttons.guest') + buttons.querySelector('#add-comment').innerText = "Add comment" + buttons.querySelector('#login').remove() + buttons.classList.remove('guest') + buttons.classList.add('active') + var textarea = document.querySelector('.add-comment textarea') + textarea.style.display = "" + textarea.required = true + document.querySelector('.add-comment .login .error').innerText = '' + delete localStorage.name + delete localStorage.email +} + +var btnLogin = document.querySelector('.add-comment .buttons.guest button#login') +if (btnLogin) { + btnLogin.addEventListener('click', event => { + event.preventDefault() + event.stopPropagation() + clearInvalid() + document.querySelector('.add-comment .buttons.guest').classList.remove('active') + document.querySelector('.add-comment .user-info').style.display = "none" + document.querySelector('.add-comment .buttons.login').classList.add('active') + var textarea = document.querySelector('.add-comment textarea') + textarea.style.display = "none" + textarea.required = false + + }) + document.querySelector('.add-comment .buttons.login button#login').addEventListener("click", event => { + event.preventDefault() + event.stopPropagation() + clearInvalid() + if (!document.querySelectorAll('.add-comment .login input:invalid').length) { + event.target.disabled = true + var data = serializeData() + login({ + "username": data.username, + "password": data.password, + }).then(response => { + if (response.error) { + document.querySelector('.add-comment .login .error').innerText = response.error + event.target.disabled = false + } else { + afterLogin() + } + }) + } else { + document.querySelectorAll('.add-comment .login input:invalid').forEach(invalid => { + invalid.classList.add('invalid') + }) + document.querySelector('.add-comment .login .error').innerText = '' + } + }) + document.querySelector('.add-comment .buttons.login button#register').addEventListener("click", event => { + event.preventDefault() + event.stopPropagation() + clearInvalid() + var login = document.querySelector('.add-comment .buttons.login button#login') + var email = document.querySelector('.add-comment .buttons.login input[name="email"]') + if (login.style.display == "none") { + if (!document.querySelectorAll('.add-comment .login input:invalid').length) { + event.target.disabled = true + var data = serializeData() + register({ + "username": data.username, + "password": data.password, + "email": data.email, + }).then(response => { + if (response.error) { + document.querySelector('.add-comment .login .error').innerText = response.error + event.target.disabled = false + } else { + afterLogin() + } + }) + + } else { + document.querySelectorAll('.add-comment .login input:invalid').forEach(invalid => { + invalid.classList.add('invalid') + }) + document.querySelector('.add-comment .login .error').innerText = '' + } + } else { + document.querySelector('.add-comment .login .error').innerText = '' + login.style.display = "none" + email.required = true + email.style.display = "block" + } + }) +} + +if (!user.username) { + if (localStorage.name) { + document.querySelector('.add-comment .comment-fields input[name="name"]').value = localStorage.name + } + if (localStorage.email) { + document.querySelector('.add-comment .comment-fields input[name="email"]').value = localStorage.email + } +} diff --git a/app/templates/item.html b/app/templates/item.html index 4c305a9..3c08f1d 100644 --- a/app/templates/item.html +++ b/app/templates/item.html @@ -5,31 +5,36 @@ {% block main %}
-