diff --git a/build.js b/build.js index f2a2f7a..9c2edb0 100644 --- a/build.js +++ b/build.js @@ -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", ] diff --git a/marko/page/data/changes.marko b/marko/page/data/changes.marko new file mode 100644 index 0000000..ae0ad56 --- /dev/null +++ b/marko/page/data/changes.marko @@ -0,0 +1,6 @@ + + <@body> + + + + diff --git a/marko/page/data/components/add-channel-modal/index.marko b/marko/page/data/components/add-channel-modal/index.marko index 82b796b..8b57be8 100644 --- a/marko/page/data/components/add-channel-modal/index.marko +++ b/marko/page/data/components/add-channel-modal/index.marko @@ -17,8 +17,6 @@ onDesc="Log new posts in this channel." offDesc="The logbot will not monitor this channel." on-change("change", "logged") /> - -

The logbot is not yet available, so this does nothing right now.

- + \ No newline at end of file diff --git a/marko/page/data/components/change/component.js b/marko/page/data/components/change/component.js new file mode 100644 index 0000000..c88926a --- /dev/null +++ b/marko/page/data/components/change/component.js @@ -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", +} \ No newline at end of file diff --git a/marko/page/data/components/change/index.marko b/marko/page/data/components/change/index.marko new file mode 100644 index 0000000..867a524 --- /dev/null +++ b/marko/page/data/components/change/index.marko @@ -0,0 +1,15 @@ +import moment from "moment"; + +
+ [${moment(input.data.date).format("MMM D HH:mm:SS")}] * + ${input.data.author} + ${state.operation} + + ${link.text} + , + + + ${state.parentLink.prefix} + ${state.parentLink.text} + +
\ No newline at end of file diff --git a/marko/page/data/components/change/style.less b/marko/page/data/components/change/style.less new file mode 100644 index 0000000..f7b2a88 --- /dev/null +++ b/marko/page/data/components/change/style.less @@ -0,0 +1,8 @@ +div.change { + text-indent: -2ch; + margin-left: 2ch; + + a:hover { + text-decoration: underline; + } +} \ No newline at end of file diff --git a/marko/page/data/components/changes-page/component.js b/marko/page/data/components/changes-page/component.js new file mode 100644 index 0000000..31883cf --- /dev/null +++ b/marko/page/data/components/changes-page/component.js @@ -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 + } +} \ No newline at end of file diff --git a/marko/page/data/components/changes-page/index.marko b/marko/page/data/components/changes-page/index.marko new file mode 100644 index 0000000..65ae451 --- /dev/null +++ b/marko/page/data/components/changes-page/index.marko @@ -0,0 +1,12 @@ + +
+
+

Changes

+

All changes to listed 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.

+ + + +
+
+ + diff --git a/marko/page/data/components/changes-page/style.less b/marko/page/data/components/changes-page/style.less new file mode 100644 index 0000000..91f0f46 --- /dev/null +++ b/marko/page/data/components/changes-page/style.less @@ -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; + } + } +} \ No newline at end of file diff --git a/marko/page/data/components/data-menu/index.marko b/marko/page/data/components/data-menu/index.marko index f2b1026..303a167 100644 --- a/marko/page/data/components/data-menu/index.marko +++ b/marko/page/data/components/data-menu/index.marko @@ -2,6 +2,7 @@ List Characters Channels + History Add diff --git a/marko/page/data/components/edit-channel-modal/index.marko b/marko/page/data/components/edit-channel-modal/index.marko index 0cd64dc..2e48937 100644 --- a/marko/page/data/components/edit-channel-modal/index.marko +++ b/marko/page/data/components/edit-channel-modal/index.marko @@ -15,7 +15,5 @@ offDesc="The logbot will not monitor this channel." on-change("change", "logged") /> -

The logbot is not yet available, so this does nothing right now.

- \ No newline at end of file diff --git a/routes/data/changes.js b/routes/data/changes.js new file mode 100644 index 0000000..fc5e79d --- /dev/null +++ b/routes/data/changes.js @@ -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 diff --git a/rpdata/api/Change.js b/rpdata/api/Change.js new file mode 100644 index 0000000..e84bf68 --- /dev/null +++ b/rpdata/api/Change.js @@ -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} + */ + 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 } + diff --git a/server.js b/server.js index 5674d32..73cc68a 100644 --- a/server.js +++ b/server.js @@ -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) {