@@ -11,6 +11,7 @@ prebuild.run({
|
||||
"./marko/page/logs/list.marko",
|
||||
"./marko/page/data/channels.marko",
|
||||
"./marko/page/data/characters.marko",
|
||||
"./marko/page/data/changes.marko",
|
||||
"./marko/page/story-content/view.marko",
|
||||
"./marko/page/logs-content/view.marko",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<include("../layout", {title: "Data", site: "data"})>
|
||||
<@body>
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<changes-page user=input.user changes=input.changes selected=input.selected />
|
||||
</@body>
|
||||
</include>
|
||||
@@ -17,8 +17,6 @@
|
||||
onDesc="Log new posts in this channel."
|
||||
offDesc="The logbot will not monitor this channel."
|
||||
on-change("change", "logged") />
|
||||
|
||||
<p class="color-danger" if(state.values.logged)>The logbot is not yet available, so this does nothing right now.</p>
|
||||
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,115 @@
|
||||
const moment = require("moment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
operation: "",
|
||||
links: [],
|
||||
parentLink: null,
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (input.data) {
|
||||
this.refresh(input.data)
|
||||
}
|
||||
}
|
||||
|
||||
refresh(data) {
|
||||
this.state.links = []
|
||||
this.state.operation = opText[data.op]
|
||||
this.state.parentLink = null
|
||||
|
||||
switch (data.model) {
|
||||
case "Channel": {
|
||||
const key = data.keys.find(k => k.id !== "*")
|
||||
|
||||
this.state.operation += " channel"
|
||||
this.state.links = [{
|
||||
text: key.id,
|
||||
href: `/data/channels/`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Character": {
|
||||
const character = data.objects.find(o => o.type === "Character")
|
||||
|
||||
this.state.operation += " character"
|
||||
this.state.links = [{
|
||||
text: `${character.name} (${character.id})`,
|
||||
href: `/data/characters/`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Story": {
|
||||
const key = data.keys.find(k => k.model === "Story" && k.id !== "*")
|
||||
const story = data.objects.find(o => o.type === "Story")
|
||||
|
||||
this.state.operation += " story"
|
||||
this.state.links = [{
|
||||
text: story.name,
|
||||
href: `/story/${key.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Chapter": {
|
||||
const storyKey = data.keys.find(k => k.model === "Story")
|
||||
const chapterKey = data.keys.find(k => k.model === "Chapter")
|
||||
const chapter = data.objects.find(o => o.type === "Chapter")
|
||||
|
||||
this.state.operation += " chapter"
|
||||
this.state.links = [{
|
||||
text: chapter.title,
|
||||
href: `/story/${storyKey.id}#${chapterKey.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Log": {
|
||||
const key = data.keys.find(k => k.id !== "*")
|
||||
|
||||
this.state.operation += " log"
|
||||
this.state.links = [{
|
||||
text: key.id,
|
||||
href: `/logs/${key.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Post": {
|
||||
const logKey = data.keys.find(k => k.model === "Log")
|
||||
const postKeys = data.keys.filter(k => k.model === "Post")
|
||||
const postObjs = data.objects.filter(o => o.type === "Post")
|
||||
|
||||
this.state.operation += (postKeys.length > 1) ? " posts" : " post"
|
||||
this.state.links = postObjs.map(po => ({
|
||||
text: `[${moment(po.time).format("HH:mm")}] ${po.nick}`,
|
||||
href: `/logs/${logKey.id}#${po.id}`,
|
||||
}))
|
||||
this.state.parentLink = {
|
||||
prefix: "in log",
|
||||
text: `${logKey.id}`,
|
||||
href: `/logs/${logKey.id}`,
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const opText = {
|
||||
add: "added",
|
||||
remove: "removed",
|
||||
move: "moved",
|
||||
edit: "edited",
|
||||
import: "imported",
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import moment from "moment";
|
||||
|
||||
<div class="change">
|
||||
<span class="change-id color-menu">[${moment(input.data.date).format("MMM D HH:mm:SS")}] * </span>
|
||||
<span class="author color-text">${input.data.author} </span>
|
||||
<span class="text color-text">${state.operation} </span>
|
||||
<span for(link in state.links | status-var=loop) key=link.href class="link">
|
||||
<a class="color-primary" href=link.href>${link.text}</a>
|
||||
<span if(loop.getIndex() < state.links.length - 1) class="comma">, </span>
|
||||
</span>
|
||||
<if (state.parentLink != null)>
|
||||
<span class="text"> ${state.parentLink.prefix} </span>
|
||||
<a class="color-primary" href=state.parentLink.href>${state.parentLink.text}</a>
|
||||
</if>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
div.change {
|
||||
text-indent: -2ch;
|
||||
margin-left: 2ch;
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
characters: input.characters,
|
||||
modal: null,
|
||||
}
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<data-menu categories=input.categories selected=(input.selected || {}) user=input.user on-open("open") />
|
||||
<main>
|
||||
<div class="changes-page">
|
||||
<h1 class="color-primary">Changes</h1>
|
||||
<p>All changes to <em>listed</em> resources is stored for 90 days for transparency reasons. The current state of an object after each change exists, but is only available through the GraphQL API at this time.</p>
|
||||
<for (change in input.changes)>
|
||||
<change data=change />
|
||||
</for>
|
||||
</div>
|
||||
</main>
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-added("characterAdded") on-close("close") />
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-close("close") />
|
||||
@@ -0,0 +1,36 @@
|
||||
.changes-page {
|
||||
width: 90%;
|
||||
max-width: 95ch;
|
||||
margin: auto;
|
||||
|
||||
> p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 1em;
|
||||
|
||||
h1 {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 0.5em 0.5ch 0.5em 0.5ch;
|
||||
opacity: 0.5;
|
||||
}
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
<menu-header>List</menu-header>
|
||||
<menu-link key="characters" selected=input.selected.characters icon="C" href="/data/characters/">Characters</menu-link>
|
||||
<menu-link key="channels" selected=input.selected.channels icon="#" href="/data/channels/">Channels</menu-link>
|
||||
<menu-link key="history" selected=input.selected.changes icon="H" href="/data/changes/">History</menu-link>
|
||||
<if-permitted user=input.user permission=["member", "character.add", "channel.add"]>
|
||||
<menu-gap />
|
||||
<menu-header>Add</menu-header>
|
||||
|
||||
@@ -15,7 +15,5 @@
|
||||
offDesc="The logbot will not monitor this channel."
|
||||
on-change("change", "logged") />
|
||||
|
||||
<p class="color-danger" if(state.values.logged)>The logbot is not yet available, so this does nothing right now.</p>
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,15 @@
|
||||
const express = require("express")
|
||||
const router = express.Router()
|
||||
|
||||
const {changesApi} = require("../../rpdata/api/Change")
|
||||
|
||||
const changesTemplate = require("../../marko/page/data/changes.marko")
|
||||
|
||||
router.get("/", async(req, res) => {
|
||||
res.markoAsync(changesTemplate, {
|
||||
changes: changesApi.list(),
|
||||
selected: {changes: true},
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
@@ -0,0 +1,81 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
class Change {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} model
|
||||
* @param {string} op
|
||||
* @param {string} author
|
||||
* @param {Date | string | number} date
|
||||
* @param {{model:string,id:string}[]} keys '
|
||||
* @param {{type:string, [x:string]:any}[]} objects
|
||||
*/
|
||||
constructor(id, model, op, author, date, keys, objects) {
|
||||
this.id = id
|
||||
this.model = model
|
||||
this.op = op
|
||||
this.author = author
|
||||
this.date = date
|
||||
this.keys = keys
|
||||
this.objects = objects
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new Change(data.id, data.model, data.op, data.author, data.date, data.keys, data.objects)
|
||||
}
|
||||
}
|
||||
|
||||
class ChangesAPI {
|
||||
/**
|
||||
* Call `channels(filter)` query
|
||||
*
|
||||
* @param {{limit:int, keys: {model:string,id:string}[]}} filter
|
||||
* @returns {Promise<Change[]>}
|
||||
*/
|
||||
list(filter = null) {
|
||||
return query(`
|
||||
query ListChanges($filter:ChangesFilter) {
|
||||
changes(filter:$filter) {
|
||||
id
|
||||
model
|
||||
op
|
||||
author
|
||||
date
|
||||
keys {
|
||||
model
|
||||
id
|
||||
}
|
||||
objects {
|
||||
type: __typename
|
||||
|
||||
...on Character {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
...on Chapter {
|
||||
id
|
||||
title
|
||||
}
|
||||
|
||||
...on Story {
|
||||
id
|
||||
name
|
||||
}
|
||||
|
||||
...on Post {
|
||||
id
|
||||
time
|
||||
nick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {filter}).then(({changes}) => {
|
||||
return changes.map(d => Change.fromData(d))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Change, changesApi: new ChangesAPI }
|
||||
|
||||
@@ -73,6 +73,7 @@ app.use("/logs/:id(L[0-9]{0,7})/", require("./routes/logs-content"))
|
||||
app.use("/data/", require("./routes/data"))
|
||||
app.use("/data/characters/", require("./routes/data/characters"))
|
||||
app.use("/data/channels/", require("./routes/data/channels"))
|
||||
app.use("/data/changes/", require("./routes/data/changes"))
|
||||
|
||||
// Entry point
|
||||
app.get("/", function(req, res) {
|
||||
|
||||
Reference in New Issue
Block a user