user page (finished?!?!)

This commit is contained in:
trinkey 2024-12-24 12:14:38 -05:00
parent 17fc3d6ffe
commit 79379ee7b2
10 changed files with 131 additions and 22 deletions

View file

@ -3,7 +3,7 @@ let timelineElement = document.getElementById("messages");
let moreButton = document.getElementById("more-button");
function escapeHTML(content) {
return content.replaceAll("&", "&amp;").replaceAll("<", "&gt;").replaceAll(">", "&lt;");
return content.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
}
function getMessageHTML(messageJSON, canRespond) {
@ -11,7 +11,7 @@ function getMessageHTML(messageJSON, canRespond) {
el.classList.add("message-container");
el.dataset.messageId = messageJSON.id;
el.innerHTML = `
<p>Messaging <a href="/u/${messageJSON.to}/">${messageJSON.to}</a>: ${messageJSON.response ? `<small>${timeSince(messageJSON.response_timestamp)}</small>` : ""}</p>
<p>Messaging <a href="/u/${messageJSON.to}/">${messageJSON.to}</a>: <small data-timestamp-here>${messageJSON.response ? timeSince(messageJSON.response_timestamp) : ""}</small></p>
<blockquote class="message">
<div><${messageJSON.from ? "a" : "b"} href="/u/${messageJSON.from}/">${messageJSON.from || "Anony&shy;mous"}</${messageJSON.from ? "a" : "b"}> writes: <small>${timeSince(messageJSON.timestamp)}</small></div>
<a class="fake-link" href="/msg/${messageJSON.id}/"><pre class="not-code">${escapeHTML(messageJSON.content)}</pre></a>
@ -25,7 +25,7 @@ function getMessageHTML(messageJSON, canRespond) {
${messageJSON.response ? "" : `<button class="reply-button" onclick="replyTo(${messageJSON.id})">Reply</button>`}
<button onclick="deleteMessage(${messageJSON.id})">Delete</button>
</div>
` : `<i>No response</i>`
` : (messageJSON.response ? "" : `<i>No response</i>`)
}`;
return el
@ -48,8 +48,13 @@ function replyTo(messageID) {
.then((response) => (response.json()))
.then((json) => {
if (json.success) {
let response = document.createElement("div");
response.innerHTML = `<small>${timeSince(json.timestamp)}</small><pre class="not-code no-margin">${escapeHTML(json.content)}</pre>`;
let response = document.createElement("pre");
response.classList.add("not-code");
response.classList.add("no-margin");
response.innerText = json.content;
msgContainer.querySelector("[data-timestamp-here]").innerHTML = timeSince(json.timestamp);
err.innerHTML = "";
msgContainer.querySelector("textarea").replaceWith(response);

View file

@ -1,5 +1,7 @@
{% extends "base.html" %}
{% block title %}Page Not Found - {% endblock %}
{% block body %}
<h1>Hmm. That doesn't look right.</h1>
Make sure the URL is correct and try again.

View file

@ -15,7 +15,7 @@
</div>
{% if username %}
Logged in as <a href="/u/{{ username }}/">{{ username }}</b>
Logged in as <a href="/u/{{ username }}/">{{ username }}</a>
<p><a href="/messages/">View your messages</a></p>
{% else %}
Not logged in.

View file

@ -8,7 +8,7 @@
{% csrf_token %}
<p><textarea class="auto-size" name="message" id="message" required maxlength="10000" placeholder="Your message"></textarea></p>
{% if self_username %}
Logged in as <a href="/u/{{ username }}/">{{ username }}</b>
Logged in as <a href="/u/{{ username }}/">{{ username }}</a>
<div>
<input name="anonymous" id="anonymous" type="checkbox">
<label data-fake-checkbox for="anonymous">Don't attach your username</label>

View file

@ -2,7 +2,7 @@
{% block head %}
<link rel="stylesheet" href="/static/css/messages.css">
<script>let url = "/api/messages/";</script>
<script>let url = "/api/messages/?unread";</script>
{% endblock %}
{% block body %}
@ -16,11 +16,11 @@
<p><button id="refresh" onclick="fetchMessages(true);">Refresh</button></p>
<p id="switch">
<b data-url="/api/messages/" data-id="all" href="javascript:(() => { updateURL('all'); })()">All messages</b> -
<a data-url="/api/messages/?unread" data-id="unread" class="not-bold" href="javascript:(() => { updateURL('unread'); })()">Not responded</a>
<a data-url="/api/messages/" data-id="all" class="not-bold" href="javascript:(() => { updateURL('all'); })()">All messages</a> -
<b data-url="/api/messages/?unread" data-id="unread" href="javascript:(() => { updateURL('unread'); })()">Not responded</b>
</p>
<div id="messages"><i class="delete-if-more-messages">Loading...</i></div>
<button hidden id="more-button" onclick="fetchMessages(false);">Load more</button>
<p><button hidden id="more-button" onclick="fetchMessages(false);">Load more</button><p>
<script src="{{ config.services.common.url.pub }}/static/js/base.js?v={{ config.version_str }}"></script>
<script src="/static/js/messages.js?v={{ config.version_str }}"></script>
<script>

View file

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block head %}
<link rel="stylesheet" href="/static/css/messages.css">
<script>let url = "/api/user/{{ username }}/?resp";</script>
{% endblock %}
{% block body %}
<h1>{{ username }}'s Messages</h1>
<p><a href="/m/{{ username }}">Message {{ username }}</a></p>
<hr>
<p><button id="refresh" onclick="fetchMessages(true);">Refresh</button></p>
<p id="switch">
<a data-url="/api/user/{{ username }}/?all" data-id="all" class="not-bold" href="javascript:(() => { updateURL('all'); })()">All messages</a> -
<b data-url="/api/user/{{ username }}/" data-id="resp" href="javascript:(() => { updateURL('resp'); })()">With response</b>
</p>
<div id="messages"><i class="delete-if-more-messages">Loading...</i></div>
<p><button hidden id="more-button" onclick="fetchMessages(false);">Load more</button><p>
<hr>
{% if self_username %}
<p>Logged in as <a href="/u/{{ self_username }}/">{{ self_username }}</a></p>
<p><a href="/messages/">View your messages</a></p>
{% else %}
Not logged in.
{% if config.new_users %}<a href="{{ config.services.auth.url.pub }}/signup/">Sign up</a>{% else %}<a href="{{ config.services.auth.url.pub }}/login/">Log in</a>{% endif %}?
{% endif %}
<script src="{{ config.services.common.url.pub }}/static/js/base.js?v={{ config.version_str }}"></script>
<script src="/static/js/messages.js?v={{ config.version_str }}"></script>
{% endblock %}

View file

@ -1,8 +1,8 @@
from django.contrib import admin
from django.urls import include, path
from .views import (api_messages, auth, index, message, message_page, messages,
profile)
from .views import (api_messages, api_user, auth, index, message, message_page,
messages, profile)
urlpatterns = [
path("", index),
@ -12,6 +12,7 @@ urlpatterns = [
path("m/<str:username>/", message),
path("msg/<int:message_id>/", message_page),
path("api/messages/", api_messages),
path("api/user/<str:username>/", api_user),
path("static/", include("tmessage.views.static")),
path("django-admin/", admin.site.urls)
]

View file

@ -1,3 +1,3 @@
from .api import api_messages # noqa: F401
from .api import api_messages, api_user # noqa: F401
from .templates import (auth, index, message, message_page, # noqa: F401
messages, profile)

View file

@ -3,10 +3,11 @@ import math
import time
from django.core.handlers.wsgi import WSGIRequest
from django.db.models import Q
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from tmessage.models import tMMessage
from tmessage.models import tMMessage, tMUser
from .helper import get_user_object, get_username
@ -18,6 +19,11 @@ def _json_response(data: dict | list, /, *, status: int=200, content_type: str="
content_type=content_type
)
def _add_qF(original: None | Q, new: Q) -> Q:
if original is None:
return new
return original | new
RESPONSE_400 = _json_response({ "success": False }, status=400)
RESPONSE_401 = _json_response({ "success": False }, status=401)
RESPONSE_403 = _json_response({ "success": False }, status=403)
@ -35,19 +41,23 @@ def api_messages(request: WSGIRequest) -> HttpResponse:
body = json.loads(request.body)
reply = body["content"].strip()
message_id = body["id"]
print(reply)
if not (isinstance(message_id, int) and isinstance(reply, str)):
if not (isinstance(message_id, int) and isinstance(reply, str) and reply):
print("1")
return RESPONSE_400
try:
message = tMMessage.objects.get(message_id=message_id)
except tMMessage.DoesNotExist:
print("2")
return RESPONSE_400
if username != message.u_to.username:
return RESPONSE_403
if message.response is not None:
print("3")
return RESPONSE_400
message.response = reply
@ -81,16 +91,16 @@ def api_messages(request: WSGIRequest) -> HttpResponse:
"success": True
})
queryFilter = {}
queryFilter = None
if "offset" in request.GET and (request.GET.get("offset") or "").isdigit():
queryFilter["message_id__lt"] = int(request.GET.get("offset") or "")
queryFilter = _add_qF(queryFilter, Q(message_id__lt=int(request.GET.get("offset") or "")))
if "unread" in request.GET:
queryFilter["response"] = None
queryFilter = _add_qF(queryFilter, Q(response=None))
if queryFilter:
msgObjects = user.received.filter(**queryFilter)
msgObjects = user.received.filter(queryFilter) # type: ignore
else:
msgObjects = user.received.all() # type: ignore
@ -116,3 +126,47 @@ def api_messages(request: WSGIRequest) -> HttpResponse:
"messages": output,
"more": msgObjects.count() > 50
})
def api_user(request: WSGIRequest, username: str) -> HttpResponse:
queryFilter = {}
try:
user = get_user_object(username)
except tMUser.DoesNotExist:
return RESPONSE_400
queryFilter = None
if "offset" in request.GET and (request.GET.get("offset") or "").isdigit():
queryFilter = _add_qF(queryFilter, Q(message_id__lt=int(request.GET.get("offset") or "")))
if "all" not in request.GET:
queryFilter = _add_qF(queryFilter, ~Q(response=None))
if queryFilter:
msgObjects = user.received.filter(queryFilter) # type: ignore
else:
msgObjects = user.received.all() # type: ignore
output = []
messages = msgObjects.order_by("-sent_timestamp" if "all" in request.GET else "-response_timestamp")[:50].values_list(
"message_id", "content", "response", "anonymous", "u_from", "u_to", "sent_timestamp", "response_timestamp"
)
for message in messages:
output.append({
"id": message[0],
"content": message[1],
"response": message[2],
"from": message[4] if not message[3] and message[4] else None,
"to": message[5],
"timestamp": message[6],
"response_timestamp": message[7]
})
return _json_response({
"success": True,
"canRespond": username == get_username(request),
"messages": output,
"more": msgObjects.count() > 50
})

View file

@ -5,7 +5,7 @@ from datetime import datetime
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse, HttpResponseRedirect
from tmessage.models import tMMessage
from tmessage.models import tMMessage, tMUser
from .helper import (get_user_object, get_username, render_template,
username_exists)
@ -36,7 +36,21 @@ def index(request: WSGIRequest) -> HttpResponse:
)
def profile(request: WSGIRequest, username: str) -> HttpResponse:
...
try:
get_user_object(username)
except tMUser.DoesNotExist:
return render_template(
request, "404.html"
)
self_username = get_username(request)
return render_template(
request, "user.html",
title=username,
username=username,
self_username=self_username
)
def message_page(request: WSGIRequest, message_id: int) -> HttpResponse:
try: