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 %}
-