ssb: Bring back the date. Whoops.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 29m37s

This commit is contained in:
Cory McWilliams 2025-04-05 22:05:26 -04:00
parent 8be354fc49
commit 657f25e22b
6 changed files with 73 additions and 35 deletions

View File

@ -55,7 +55,7 @@ standard.
## Running ## Running
By default, running the built `out/debug/tildefriends` executable will start a By default, running the built `out/debug/tildefriends` executable will start a
web server at <http://localhost:12345/>. `tildefriends -h` lists further web server at <http://localhost:12345/>. `tildefriends -h` lists further
options. options.
The first user to create an account and log in will be granted administrative The first user to create an account and log in will be granted administrative

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🦀", "emoji": "🦀",
"previous": "&lGTfidaEuVfuFbpQD9WGizMpC6OwYr1AqM+AH0yaVsc=.sha256" "previous": "&ErfmEIt3WboZp6PR3cC/i7XU/ZTI/2X5I9XvUN0GGMU=.sha256"
} }

View File

@ -377,9 +377,7 @@ class TfMessageElement extends LitElement {
render_menu() { render_menu() {
let content = this.get_content(); let content = this.get_content();
let formats = [ let formats = [['message', 'Message']];
['message', 'Message'],
];
if (content?.type == 'post' || content?.type == 'blog') { if (content?.type == 'post' || content?.type == 'blog') {
formats.push(['md', 'Markdown']); formats.push(['md', 'Markdown']);
} }
@ -390,12 +388,33 @@ class TfMessageElement extends LitElement {
return html` return html`
<div class="w3-bar-item w3-right w3-dropdown-hover"> <div class="w3-bar-item w3-right w3-dropdown-hover">
<button class="w3-button w3-theme-d1">%</button> <button class="w3-button w3-theme-d1">%</button>
<div class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1" style="right: 48px"> <div
<a target="_top" class="w3-button w3-bar-item" href=${'#' + encodeURIComponent(this.message?.id)}>${this.message?.id}</a> class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
<button class="w3-button w3-bar-item w3-border-bottom" @click=${this.copy_id}>Copy ID</button> style="right: 48px"
${formats.map(([format, name]) => (html` >
<button class="w3-button w3-bar-item" style=${format == this.format ? 'font-weight: bold' : ''} @click=${() => (this.format = format)}>${name}</button> <a
`))} target="_top"
class="w3-button w3-bar-item"
href=${'#' + encodeURIComponent(this.message?.id)}
>${this.message?.id}</a
>
<button
class="w3-button w3-bar-item w3-border-bottom"
@click=${this.copy_id}
>
Copy ID
</button>
${formats.map(
([format, name]) => html`
<button
class="w3-button w3-bar-item"
style=${format == this.format ? 'font-weight: bold' : ''}
@click=${() => (this.format = format)}
>
${name}
</button>
`
)}
</div> </div>
</div> </div>
`; `;
@ -412,11 +431,10 @@ class TfMessageElement extends LitElement {
<span class="w3-bar-item"> <span class="w3-bar-item">
<tf-user id=${this.message.author} .users=${this.users}></tf-user> <tf-user id=${this.message.author} .users=${this.users}></tf-user>
</span> </span>
${is_encrypted} ${is_encrypted} ${this.render_menu()}
${this.render_menu()} <div class="w3-bar-item w3-right" style="text-wrap: nowrap">
<span class="w3-bar-item w3-right" style="text-wrap: nowrap" ${new Date(this.message.timestamp).toLocaleString()}
${new Date(this.message.timestamp).toLocaleString()}</span </div>
>
</header> </header>
`; `;
} }

View File

@ -189,7 +189,7 @@ class TfTabNewsFeedElement extends LitElement {
UNION UNION
SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id
`, `,
[JSON.stringify(result.map(x => x.id))] [JSON.stringify(result.map((x) => x.id))]
); );
let t2 = new Date(); let t2 = new Date();
let related_messages = await tfrpc.rpc.query( let related_messages = await tfrpc.rpc.query(
@ -199,7 +199,8 @@ class TfTabNewsFeedElement extends LitElement {
JOIN json_each(?2) refs ON messages.id = refs.value JOIN json_each(?2) refs ON messages.id = refs.value
JOIN json_each(?1) AS following ON messages.author = following.value JOIN json_each(?1) AS following ON messages.author = following.value
`, `,
[JSON.stringify(this.following), JSON.stringify(refs.map(x => x.ref))]); [JSON.stringify(this.following), JSON.stringify(refs.map((x) => x.ref))]
);
result = [].concat(result, related_messages); result = [].concat(result, related_messages);
let t3 = new Date(); let t3 = new Date();
console.log( console.log(

View File

@ -2,7 +2,7 @@ let g_hash;
async function query(sql, params) { async function query(sql, params) {
let results = []; let results = [];
await ssb.sqlAsync(sql, params, function(row) { await ssb.sqlAsync(sql, params, function (row) {
results.push(row); results.push(row);
}); });
return results; return results;
@ -35,7 +35,10 @@ async function resolve(id) {
} }
async function get_names(identities) { async function get_names(identities) {
return Object.fromEntries((await query(` return Object.fromEntries(
(
await query(
`
SELECT author, name FROM ( SELECT author, name FROM (
SELECT SELECT
messages.author, messages.author,
@ -47,23 +50,35 @@ async function get_names(identities) {
json_extract(messages.content, '$.type') = 'about' AND json_extract(messages.content, '$.type') = 'about' AND
content ->> 'about' = messages.author AND name IS NOT NULL) content ->> 'about' = messages.author AND name IS NOT NULL)
WHERE author_rank = 1 WHERE author_rank = 1
`, [JSON.stringify(identities)])).map(x => [x.author, x.name])); `,
[JSON.stringify(identities)]
)
).map((x) => [x.author, x.name])
);
} }
async function render(hash) { async function render(hash) {
g_hash = hash; g_hash = hash;
if (!hash) { if (!hash) {
let sites = (await query(` let sites = await query(
`
SELECT site.author, site.id SELECT site.author, site.id
FROM messages site FROM messages site
WHERE site.content ->> 'type' = 'web-init' WHERE site.content ->> 'type' = 'web-init'
`, [])); `,
let names = await get_names(sites.map(x => x.author)); []
);
let names = await get_names(sites.map((x) => x.author));
if (hash === g_hash) { if (hash === g_hash) {
await app.setDocument(`<ul style="background-color: #ddd">${sites.map(x => `<li><a target="_top" href="#${encodeURIComponent(x.id)}">${names[x.author] ?? x.author} - ${x.id}</a></li>`).join('\n')}</ul>`); await app.setDocument(
`<ul style="background-color: #ddd">${sites.map((x) => `<li><a target="_top" href="#${encodeURIComponent(x.id)}">${names[x.author] ?? x.author} - ${x.id}</a></li>`).join('\n')}</ul>`
);
} }
} else { } else {
let site_id = hash.charAt(0) == '#' ? decodeURIComponent(hash.substring(1)) : decodeURIComponent(hash); let site_id =
hash.charAt(0) == '#'
? decodeURIComponent(hash.substring(1))
: decodeURIComponent(hash);
await app.setDocument(`<html style="margin: 0; padding: 0; width: 100vw; height: 100vh; margin: 0; padding: 0"> await app.setDocument(`<html style="margin: 0; padding: 0; width: 100vw; height: 100vh; margin: 0; padding: 0">
<body style="display: flex; flex-direction: column; width: 100vw; height: 100vh"> <body style="display: flex; flex-direction: column; width: 100vw; height: 100vh">
<iframe src="${encodeURIComponent(site_id)}/index.html" style="flex: 1 1; border: 0; background-color: #fff"></iframe> <iframe src="${encodeURIComponent(site_id)}/index.html" style="flex: 1 1; border: 0; background-color: #fff"></iframe>
@ -82,4 +97,4 @@ async function main() {
render(null); render(null);
} }
main(); main();

View File

@ -1,6 +1,6 @@
async function query(sql, params) { async function query(sql, params) {
let results = []; let results = [];
await ssb.sqlAsync(sql, params, function(row) { await ssb.sqlAsync(sql, params, function (row) {
results.push(row); results.push(row);
}); });
return results; return results;
@ -19,17 +19,22 @@ function guess_content_type(name) {
} }
async function main() { async function main() {
let path = request.path.replaceAll(/(%[0-9a-fA-F]{2})/g, x => String.fromCharCode(parseInt(x.substring(1), 16))); let path = request.path.replaceAll(/(%[0-9a-fA-F]{2})/g, (x) =>
String.fromCharCode(parseInt(x.substring(1), 16))
);
let match = path.match(/^(%.{44}\.sha256)(?:\/)?(.*)$/); let match = path.match(/^(%.{44}\.sha256)(?:\/)?(.*)$/);
let content_type = guess_content_type(request.path); let content_type = guess_content_type(request.path);
let root = await query(` let root = await query(
`
SELECT root.content ->> 'root' AS root SELECT root.content ->> 'root' AS root
FROM messages site FROM messages site
JOIN messages root JOIN messages root
ON site.id = ? AND root.author = site.author AND root.content ->> 'site' = site.id ON site.id = ? AND root.author = site.author AND root.content ->> 'site' = site.id
ORDER BY root.sequence DESC LIMIT 1 ORDER BY root.sequence DESC LIMIT 1
`, [match[1]]); `,
[match[1]]
);
let root_id = root[0]['root']; let root_id = root[0]['root'];
let last_id = root_id; let last_id = root_id;
let blob = await ssb.blobGet(root_id); let blob = await ssb.blobGet(root_id);
@ -40,8 +45,7 @@ async function main() {
blob = await ssb.blobGet(dir?.links[part]); blob = await ssb.blobGet(dir?.links[part]);
content_type = guess_content_type(part); content_type = guess_content_type(part);
} }
} catch { } catch {}
}
respond({ respond({
status_code: 200, status_code: 200,
@ -50,10 +54,10 @@ async function main() {
}); });
} }
main().catch(function(e) { main().catch(function (e) {
respond({ respond({
status_code: 200, status_code: 200,
data: `${e.message}\n${e.stack}`, data: `${e.message}\n${e.stack}`,
content_type: 'text/plain', content_type: 'text/plain',
}); });
}); });