tildefriends/packages/cory/blog/blog.js

198 lines
5.3 KiB
JavaScript
Raw Normal View History

"use strict";
//! {"category": "work in progress", "require": ["libdocument", "liblist", "ui"]}
let libdocument = require("libdocument");
let liblist = require("liblist");
let libui = require("ui");
class Blog {
constructor() {
this._list = liblist.ListStore("blog:list");
this._documents = libdocument.DocumentStore("blog:posts");
}
async renderIndex() {
terminal.cork();
try {
terminal.split([{name: "terminal"}]);
terminal.clear();
let posts = await this._list.get(-1, -10);
for (let i = 0; i < posts.length; i++) {
let post = await this._documents.get(posts[i]);
if (post) {
let formatted = this.formatPost(post);
for (let j in formatted) {
terminal.print(formatted[j]);
}
}
}
if (core.user.credentials.permissions.administration) {
terminal.print({command: JSON.stringify({action: "new"}), value: "new post"});
}
} finally {
terminal.uncork();
}
}
formatPost(post) {
let result = [
[{style: "font-size: xx-large", value: post.title}],
[{style: "font-size: x-small", value: post.author}, " ", {style: "font-size: x-small", value: post.created}],
post.body,
];
if (core.user.credentials.permissions.administration) {
result[0].push({command: JSON.stringify({action: "edit", post: post.name}), value: "edit"});
}
return result;
}
async submitPost(post) {
let now = new Date();
let oldPost = await this._documents.get(post.name);
if (!await this._list.getByKey(post.name)) {
this._list.push(post.name);
}
for (let key in oldPost) {
if (!post[key]) {
post[key] = oldPost[key];
}
}
if (!post.created) {
post.created = now;
}
if (!post.author) {
post.author = core.user.name;
}
post.modified = now;
await this._documents.set(post.name, post);
}
async deletePost(name) {
await this._documents.set(name, null);
}
async handleCommand(command) {
if (command.action == "edit") {
await this.edit(command.post);
} else if (command.action == "new") {
await this.edit(null);
} else if (command.action == "delete") {
await this.deletePost(command.post);
}
}
async edit(page) {
terminal.cork();
try {
let self = this;
self._post = await this._documents.get(page);
if (!this._onWindowMessage) {
this._onWindowMessage = function(event) {
let message = event.message;
if (message == "load") {
terminal.postMessageToIframe("iframe", self._post);
} else if (message.action == "save") {
self.submitPost(message.post).then(self.renderIndex.bind(self)).catch(terminal.print);
} else if (message.action == "delete") {
self.deletePost(message.post).then(self.renderIndex.bind(self)).catch(terminal.print);
} else if (message.action == "back") {
self.renderIndex.bind(self)().catch(terminal.print);
}
}
core.register("onWindowMessage", this._onWindowMessage);
}
terminal.split([{name: "terminal", type: "vertical"}]);
terminal.clear();
terminal.print({iframe: `
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.13.2/codemirror.min.css"></link>
<style>
html, body, #contents {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
color: #fff;
}
.CodeMirror {
width: 100%;
height: 100%;
}
.CodeMirror-scroll {
}
</style>
</head>
<body>
<div>
<input type="button" id="back" value="Back" onclick="back()">
<input type="button" id="save" value="Save" onclick="save()">
<input type="button" id="delete" value="Delete" onclick="deletePost()">
</div>
<div><label for="name">Name:</label> <input type="text" id="name"></div>
<div><label for="title">Title:</label> <input type="text" id="title"></div>
<textarea id="contents" style="width: 100%; height: 100%"></textarea>
<script>
var gEditor;
window.addEventListener("message", function(event) {
var message = event.data;
console.debug(message);
gEditor.setValue(message.body || "");
document.getElementById("name").value = message.name || "untitled";
document.getElementById("title").value = message.title || "untitled";
});
window.addEventListener("load", function() {
gEditor = CodeMirror.fromTextArea(document.getElementById("contents"), {
lineNumbers: true
});
//gEditor.on("change", textChanged);
parent.postMessage("load", "*");
});
function back() {
parent.postMessage({action: "back"}, "*");
}
function save() {
parent.postMessage({
action: "save",
post: {
name: document.getElementById("name").value,
title: document.getElementById("title").value,
body: gEditor.getValue(),
},
}, "*");
}
function deletePost() {
parent.postMessage({
action: "delete",
post: document.getElementById("name").value,
}, "*");
}
</script>
</body>
</html>
`, name: "iframe", style: "flex: 1 1 auto; width: 100%; margin: 0; border: 0; padding: 0;"});
} finally {
terminal.uncork();
}
}
}
terminal.setEcho(false);
let blog = new Blog();
blog.renderIndex().catch(terminal.print);
core.register("onInput", async function(input) {
try {
await blog.handleCommand(JSON.parse(input));
} catch (error) {
terminal.print(error);
}
});