5 Commits

10 changed files with 79 additions and 11 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.env
users.yaml

View File

@@ -48,6 +48,12 @@ code {
padding: 0.1em 0.3em;
}
pre {
overflow-x: auto;
white-space: pre;
max-width: 100%;
}
pre code {
display: block;
padding: 0.6rem 1rem;
@@ -74,6 +80,14 @@ main header {
margin-bottom: 3rem;
}
main header .draft-label {
display: inline-block;
padding: 0.5rem 1rem;
border: 1px solid;
border-radius: 5px;
font-weight: bold;
}
main header h1 {
margin-bottom: 1.6rem;
}
@@ -99,7 +113,11 @@ main .article-list .item {
}
main .article-list .item h3 {
margin-bottom: 1rem;
margin-bottom: 0.5rem;
}
main .article-list p {
margin-top: 0.5rem;
}
main article footer {

View File

@@ -20,6 +20,11 @@ code {
color: #027739;
}
pre {
background-color: #333;
color: #ccc;
}
pre code {
background-color: #333;
color: #ccc;
@@ -29,6 +34,11 @@ dl dt {
color: #888;
}
main header .draft-label {
color: #770202;
border-color: #770202;
}
main header .meta .date {
color: #888;
}

11
html.ts
View File

@@ -40,10 +40,16 @@ export function errorPageHtml(statusCode: number, title: string): string {
export function articleHtml(article: Article, profile: Profile): string {
const publishedAtFormatted = localizeDate(article.publishedAt);
const pageTitle = article.isDraft ? `Draft: ${article.title}` : article.title;
let draftLabel = ``;
if (article.isDraft) {
draftLabel = `<p class="draft-label">Draft version</p>`;
}
const body = `
<main>
<header>
${draftLabel}
<h1>${article.title}</h1>
<div class="meta">
<img class="avatar" src="${profile.picture}" alt="User Avatar" />
@@ -62,7 +68,7 @@ export function articleHtml(article: Article, profile: Profile): string {
</main>
`;
return htmlLayout(article.title, body, profile);
return htmlLayout(pageTitle, body, profile);
}
function articleListItemHtml(article: Article): string {
@@ -78,9 +84,10 @@ function articleListItemHtml(article: Article): string {
export function articleListHtml(articles: Article[]): string {
if (articles.length === 0) return "";
const sortedArticles = articles.sort((a, b) => b.publishedAt - a.publishedAt);
let html = "";
for (const article of articles) {
for (const article of sortedArticles) {
html += articleListItemHtml(article);
}

View File

@@ -15,6 +15,10 @@ export default class Article {
return tag ? tag[1] : "";
}
get isDraft(): boolean {
return this.event.kind === 30024;
}
get url(): string {
return `${config.base_url}/${this.naddr}`;
}
@@ -47,6 +51,7 @@ export default class Article {
identifier: this.identifier,
pubkey: this.event.pubkey,
kind: this.event.kind,
relays: [config.home_relay_url],
});
}
}

View File

@@ -2,7 +2,9 @@ import { nip19 } from "@nostr/tools";
import { NEvent } from "../nostr.ts";
export interface ProfileData {
name: string;
name?: string;
display_name?: string;
displayName?: string;
about?: string;
picture?: string;
nip05?: string;

View File

@@ -17,14 +17,25 @@ export async function fetchReplaceableEvent(
pubkey: string,
identifier: string,
) {
const events = await relay.query([{
let events = await relay.query([{
authors: [pubkey],
kinds: [30023],
"#d": [identifier],
limit: 1,
}]);
return events.length > 0 ? events[0] : null;
if (events.length > 0) {
return events[0];
} else {
events = await relay.query([{
authors: [pubkey],
kinds: [30024],
"#d": [identifier],
limit: 1,
}]);
return events.length > 0 ? events[0] : null;
}
}
export async function fetchArticlesByAuthor(pubkey: string) {

View File

@@ -20,6 +20,18 @@ describe("Article", () => {
});
});
describe("#isDraft", () => {
it("is false when kind is 30023", () => {
expect(article.isDraft).toBe(false);
});
it("is true when kind is 30024", () => {
article.event.kind = 30024;
expect(article.isDraft).toBe(true);
article.event.kind = 30023;
});
});
describe("#title", () => {
it("returns the content of the 'title' tag", () => {
expect(article.title).toMatch(
@@ -53,9 +65,9 @@ describe("Article", () => {
});
describe("#naddr", () => {
it("returns bech32 addressable event ID", () => {
expect(article.naddr).toEqual(
"naddr1qvzqqqr4gupzq8meqkx80g3yuklzymy0qfx2ekk56aqc2ht4ak03z3em4r4cdcwtqqxnzdejxcenjd3hx5urgwp4676hkz",
it("returns a bech32 addressable event ID", () => {
expect(article.naddr).toMatch(
/naddr1qvzqqqr4gupzq8meqkx80g3yuklzymy0qf/,
);
});
});

View File

@@ -1,2 +0,0 @@
_: b3e1b7c0ef48294bd856203bfd460625de95d3afb894e5f09b14cd1f0e7097cf
accounts: b3e1b7c1660b7db0ecb93ec55c09e67961171a5c4e9e2602f1b47477ea61c50a

4
users.yaml.sample Normal file
View File

@@ -0,0 +1,4 @@
_: b3e1b7c0ef48294bd856203bfd460625de95d3afb894e5f09b14cd1f0e7097cf
accounts: b3e1b7c1660b7db0ecb93ec55c09e67961171a5c4e9e2602f1b47477ea61c50a
bitcoincore: 47750177bb6bb113784e4973f6b2e3dd27ef1eff227d6e38d0046d618969e41a
# jeffg: 1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef