Live times
This commit is contained in:
parent
a66207cb9b
commit
51208f43b3
3 changed files with 78 additions and 15 deletions
60
cry/static/index.js
Normal file
60
cry/static/index.js
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
console.log("Hello world!");
|
||||||
|
|
||||||
|
function time_ago(time) {
|
||||||
|
// Much like the python in feed.py
|
||||||
|
const now = Date.now();
|
||||||
|
const seconds = Math.round((now - time) / 1000);
|
||||||
|
|
||||||
|
// If you *must* have live output (for debugging or something) just
|
||||||
|
// uncomment here.
|
||||||
|
// return `${seconds}s`;
|
||||||
|
|
||||||
|
if (seconds < 90) {
|
||||||
|
return `${seconds}s`;
|
||||||
|
}
|
||||||
|
const minutes = Math.round(seconds / 60);
|
||||||
|
if (minutes < 90) {
|
||||||
|
return `${minutes}m`;
|
||||||
|
}
|
||||||
|
const hours = Math.round(minutes / 60);
|
||||||
|
if (hours < 24) {
|
||||||
|
return `${hours}h`;
|
||||||
|
}
|
||||||
|
const days = Math.round(hours / 24);
|
||||||
|
if (days <= 7) {
|
||||||
|
return `${days}d`;
|
||||||
|
}
|
||||||
|
const weeks = Math.round(days / 7);
|
||||||
|
if (weeks < 52) {
|
||||||
|
return `${weeks}w`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const years = Math.round(weeks / 52);
|
||||||
|
return `{years}y`
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeAgoTick(selector, format) {
|
||||||
|
const tags = document.querySelectorAll(selector);
|
||||||
|
for (const tag of tags) {
|
||||||
|
const then = Date.parse(tag.getAttribute("datetime"));
|
||||||
|
const when = time_ago(then);
|
||||||
|
const formatted = format(when);
|
||||||
|
|
||||||
|
// NOTE: I'm being a real dork about this because I am super nervous
|
||||||
|
// about leaking memory/making the page heaver than it needs to be,
|
||||||
|
// with a callback every second and all.
|
||||||
|
const text = tag.firstChild;
|
||||||
|
if (text.data != formatted) {
|
||||||
|
text.data = formatted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerTimeCallback() {
|
||||||
|
setInterval(() => {
|
||||||
|
timeAgoTick(".time-ago-bare", (a) => a);
|
||||||
|
timeAgoTick(".time-ago-paren", (a) => `(${a})`);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("DOMContentLoaded", () => registerTimeCallback());
|
||||||
11
cry/views.py
11
cry/views.py
|
|
@ -41,7 +41,10 @@ def _feed_summary(f: feed.Feed) -> tags.li:
|
||||||
{"class": "feed-summary-timestamp"},
|
{"class": "feed-summary-timestamp"},
|
||||||
tags.i(
|
tags.i(
|
||||||
f"Posted ",
|
f"Posted ",
|
||||||
time_(entry.time_ago(), datetime=entry.posted_time_iso()),
|
time_(
|
||||||
|
{"class": "time-ago-bare", "datetime": entry.posted_time_iso()},
|
||||||
|
entry.time_ago(),
|
||||||
|
),
|
||||||
" ago",
|
" ago",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -116,8 +119,11 @@ def feed_view(feeds: list[feed.Feed]) -> tags.html:
|
||||||
),
|
),
|
||||||
" ",
|
" ",
|
||||||
time_(
|
time_(
|
||||||
|
{
|
||||||
|
"class": "time-ago-paren",
|
||||||
|
"datetime": entry.posted_time_iso(),
|
||||||
|
},
|
||||||
f"({entry.time_ago()})",
|
f"({entry.time_ago()})",
|
||||||
datetime=entry.posted_time_iso(),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for entry in f.entries
|
for entry in f.entries
|
||||||
|
|
@ -131,6 +137,7 @@ def feed_view(feeds: list[feed.Feed]) -> tags.html:
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
tags.script({"src": "/index.js"}),
|
||||||
)
|
)
|
||||||
assert isinstance(document, tags.html)
|
assert isinstance(document, tags.html)
|
||||||
return document
|
return document
|
||||||
|
|
|
||||||
22
cry/web.py
22
cry/web.py
|
|
@ -1,10 +1,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import dominate.tags as tags
|
|
||||||
import functools
|
import functools
|
||||||
import http.server
|
import http.server
|
||||||
import io
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
@ -17,6 +15,7 @@ from . import database
|
||||||
from . import feed
|
from . import feed
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
class DeadlineCondition:
|
class DeadlineCondition:
|
||||||
"""A condition variable that allows you to wait with a timeout."""
|
"""A condition variable that allows you to wait with a timeout."""
|
||||||
|
|
||||||
|
|
@ -296,6 +295,7 @@ def refresh_feeds(sink: EventChannel):
|
||||||
|
|
||||||
REFRESH_TASK: BackgroundTask | None = None
|
REFRESH_TASK: BackgroundTask | None = None
|
||||||
|
|
||||||
|
|
||||||
@background_task
|
@background_task
|
||||||
def subscribe(sink: EventChannel, url: str):
|
def subscribe(sink: EventChannel, url: str):
|
||||||
"""Subscribe to a feed."""
|
"""Subscribe to a feed."""
|
||||||
|
|
@ -338,9 +338,11 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||||
if self.path == "/":
|
if self.path == "/":
|
||||||
return self.serve_feeds()
|
return self.serve_feeds()
|
||||||
elif self.path == "/style.css":
|
elif self.path == "/style.css":
|
||||||
return self.serve_style()
|
return self.serve_static("style.css", "text/css")
|
||||||
elif self.path == "/event.js":
|
elif self.path == "/event.js":
|
||||||
return self.serve_event_js()
|
return self.serve_static("event.js", "text/javascript")
|
||||||
|
elif self.path == "/index.js":
|
||||||
|
return self.serve_static("index.js", "text/javascript")
|
||||||
elif self.path == "/refresh-status":
|
elif self.path == "/refresh-status":
|
||||||
return self.serve_status()
|
return self.serve_status()
|
||||||
elif self.path == "/subscribe-status":
|
elif self.path == "/subscribe-status":
|
||||||
|
|
@ -478,16 +480,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(response)
|
self.wfile.write(response)
|
||||||
|
|
||||||
def serve_event_js(self):
|
def serve_static(self, name: str, content_type: str):
|
||||||
self.write_file(
|
self.write_file(
|
||||||
pathlib.Path(__file__).parent / "static" / "event.js",
|
pathlib.Path(__file__).parent / "static" / name,
|
||||||
content_type="text/javascript",
|
content_type=content_type,
|
||||||
)
|
|
||||||
|
|
||||||
def serve_style(self):
|
|
||||||
self.write_file(
|
|
||||||
pathlib.Path(__file__).parent / "static" / "style.css",
|
|
||||||
content_type="text/css",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def write_file(self, path: pathlib.Path, content_type: str):
|
def write_file(self, path: pathlib.Path, content_type: str):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue