message page

This commit is contained in:
trinkey 2024-12-24 11:34:10 -05:00
parent 156a9becc1
commit 17fc3d6ffe
10 changed files with 77 additions and 43 deletions

View file

@ -21,6 +21,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
STATIC_DIR = BASE_DIR / "tmessage/static"
ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = [config["services"]["message"]["url"]["pub"]]
INSTALLED_APPS = [
"django.contrib.admin",

View file

@ -2,6 +2,7 @@
width: calc(90vw - 40px);
max-width: 400px;
padding: 20px;
padding-top: 2px;
margin: 0 auto;
margin-bottom: 30px;
border-radius: 6px;

View file

@ -10,11 +10,13 @@ function getMessageHTML(messageJSON, canRespond) {
let el = document.createElement("div");
el.classList.add("message-container");
el.dataset.messageId = messageJSON.id;
el.innerHTML = `<blockquote class="message">
<div><b>${messageJSON.from || "Anonymous"}</b> writes: <small>${timeSince(messageJSON.timestamp)}</small></div>
<pre class="not-code">${escapeHTML(messageJSON.content)}</pre>
el.innerHTML = `
<p>Messaging <a href="/u/${messageJSON.to}/">${messageJSON.to}</a>: ${messageJSON.response ? `<small>${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>
</blockquote>
${messageJSON.response ? `<small>${timeSince(messageJSON.response_timestamp)}</small><pre class="not-code no-margin">\n${escapeHTML(messageJSON.response)}</pre>` : ""}
${messageJSON.response ? `<a class="fake-link" href="/msg/${messageJSON.id}/"><pre class="not-code no-margin">${escapeHTML(messageJSON.response)}</pre></a>` : ""}
${
canRespond ? `
<div class="msg-error"></div>
@ -29,31 +31,6 @@ function getMessageHTML(messageJSON, canRespond) {
return el
}
function timeSince(date) {
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
let dateObject = new Date(date * 1000);
let dateString = `${months[dateObject.getMonth()]} ${dateObject.getDate()}, ${dateObject.getFullYear()}, ${String(dateObject.getHours()).padStart(2, "0")}:${String(dateObject.getMinutes()).padStart(2, "0")}:${String(dateObject.getSeconds()).padStart(2, "0")}`;
let seconds = Math.floor((+(new Date()) / 1000 - date + 1));
let unit = "second";
let amount = seconds > 0 ? seconds : 0;
const timeAmounts = [
{ name: "minute", amount: 60 },
{ name: "hour", amount: 3600 },
{ name: "day", amount: 86400 },
{ name: "month", amount: 2592000 },
{ name: "year", amount: 31536000 }
];
for (const info of timeAmounts) {
let interval = seconds / info.amount;
if (interval >= 1) {
unit = info.name;
amount = Math.floor(interval);
}
}
return `<span data-timestamp="${date}" title="${dateString}">${Math.floor(amount)} ${unit}${Math.floor(amount) == 1 ? "" : "s"} ago</span>`;
}
function replyTo(messageID) {
let msgContainer = document.querySelector(`.message-container[data-message-id="${messageID}"]`);
let self = msgContainer.querySelector(".reply-button")
@ -197,9 +174,3 @@ function fetchMessages(fetchFromStart=false) {
}
fetchMessages(true);
setInterval(function () {
for (const timestamp of document.querySelectorAll("[data-timestamp]")) {
timestamp.innerHTML = timeSince(Number(timestamp.dataset.timestamp));
}
}, 5000);

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block head %}
<link rel="stylesheet" href="/static/css/messages.css?v={{ config.version_str }}">
{% endblock %}
{% block body %}
<div class="message-container">
<p>Messaging <a href="/u/{{ message.u_to.username }}/">{{ message.u_to.username }}</a>: {% if message.response %}<small><span data-timestamp="{{ message.response_timestamp }}"></span></small>{% endif %}</p>
<blockquote class="message">
<div>{% if not message.anonymous and message.u_from.username %}<a href="/u/{{ message.u_from.username }}/">{{ message.u_from.username }}</a>{% else %}<b>Anony&shy;mous</b>{% endif %} writes: <small><span data-timestamp="{{ message.sent_timestamp }}"></span></small></div>
<pre class="not-code">{{ message.content }}</pre>
</blockquote>
<pre class="not-code no-margin">{{ message.response }}</pre>
</div>
{% if username %}
Logged in as <a href="/u/{{ username }}/">{{ username }}</b>
<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>
{% endblock %}

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 <b>{{ self_username }}</b>
Logged in as <a href="/u/{{ username }}/">{{ username }}</b>
<div>
<input name="anonymous" id="anonymous" type="checkbox">
<label data-fake-checkbox for="anonymous">Don't attach your username</label>
@ -18,5 +18,6 @@
{% 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 %}
<p><input type="submit" value="Send message"></p>
{% if self_username %}<p><a href="/messages/">View your messages</a></p>{% endif %}
</form>
{% endblock %}

View file

@ -7,14 +7,27 @@
{% block body %}
<h1>Your messages</h1>
Logged in as <b>{{ username }}</b>
<p>Logged in as <a href="/u/{{ username }}">{{ username }}</a></p>
<hr>
<button id="refresh" onclick="fetchMessages(true);">Refresh</button>
<p>
<div>Your message link: <code class="cursor-pointer" id="message-link">{{ config.services.message.url.pub }}/m/{{ username }}/</code></div>
<div><small>(click to copy)</small></div>
</p>
<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>
</p>
<div id="messages"><i class="delete-if-more-messages">Loading...</i></div>
<button hidden id="more-button" onclick="fetchMessages(false);">Load more</button>
<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>
document.getElementById("message-link").addEventListener("click", function() {
navigator.clipboard.writeText(this.innerText);
this.classList.remove("success-anim");
setTimeout(() => { this.classList.add("success-anim"); }, 10);
});
</script>
{% endblock %}

View file

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

View file

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

View file

@ -96,7 +96,7 @@ def api_messages(request: WSGIRequest) -> HttpResponse:
output = []
messages = msgObjects.order_by("-message_id")[:50].values_list(
"message_id", "content", "response", "anonymous", "u_from", "sent_timestamp", "response_timestamp"
"message_id", "content", "response", "anonymous", "u_from", "u_to", "sent_timestamp", "response_timestamp"
)
for message in messages:
@ -105,8 +105,9 @@ def api_messages(request: WSGIRequest) -> HttpResponse:
"content": message[1],
"response": message[2],
"from": message[4] if not message[3] and message[4] else None,
"timestamp": message[5],
"response_timestamp": message[6]
"to": message[5],
"timestamp": message[6],
"response_timestamp": message[7]
})
return _json_response({

View file

@ -38,6 +38,22 @@ def index(request: WSGIRequest) -> HttpResponse:
def profile(request: WSGIRequest, username: str) -> HttpResponse:
...
def message_page(request: WSGIRequest, message_id: int) -> HttpResponse:
try:
message = tMMessage.objects.get(message_id=message_id)
except tMMessage.DoesNotExist:
return render_template(
request, "404.html",
status=404
)
return render_template(
request, "message-page.html",
title="Viewing a Message",
message=message,
username=get_username(request)
)
def message(request: WSGIRequest, username: str) -> HttpResponse:
if not username_exists(username):
return render_template(
@ -73,6 +89,7 @@ def message(request: WSGIRequest, username: str) -> HttpResponse:
return render_template(
request, "message.html",
title=f"Messaging {username}",
username=username,
error=error,
self_username=get_username(request)
@ -86,5 +103,6 @@ def messages(request: WSGIRequest) -> HttpResponse:
return render_template(
request, "messages.html",
title="Messages",
username=username
)