logs, logs-content: Added basic view of logs and added characters to logs
This commit is contained in:
@@ -12,11 +12,12 @@ div.annotation {
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
p {
|
||||
p, div.markdown-content > p {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
p:first-of-type {
|
||||
p:first-of-type, div.markdown-content > p:first-of-type {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,15 @@
|
||||
<div class="title">
|
||||
<a class="color-primary" href=input.link>${input.name}</a>
|
||||
</div>
|
||||
<div class="description color-text">${input.description || ""}</div>
|
||||
<div if(input.description) class="description color-text">
|
||||
<div class="content">${input.description}</div>
|
||||
</div>
|
||||
<div if(input.characters) class="characters">
|
||||
<div for(character in (input.characters || [])) class="character color-menu">
|
||||
<span class="name">${input.characters.length > 6 ? character.shortName : character.name}</span>
|
||||
<span class="comma">,</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<item-meta kind="date" value=input.createdDate updated=input.updatedDate />
|
||||
<item-meta kind="date" value=input.fictionalDate />
|
||||
|
||||
@@ -34,6 +34,21 @@ tr.content-list-item {
|
||||
}
|
||||
}
|
||||
|
||||
> .characters {
|
||||
margin: 0 1ch;
|
||||
|
||||
> .character {
|
||||
display: inline-block;
|
||||
margin-right: 1ch;
|
||||
}
|
||||
|
||||
> .character:last-of-type {
|
||||
.comma {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .meta {
|
||||
> div {
|
||||
display: inline-block;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<div class=["markdown-content", "color-text", input.class]>
|
||||
<div class=["markdown-content", input.class]>
|
||||
<include(state.render) />
|
||||
</div>
|
||||
@@ -1,6 +1,5 @@
|
||||
div.markdown-content {
|
||||
line-height: 1.333em;
|
||||
font-size: 1.25em;
|
||||
line-height: 1.40em;
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
|
||||
@@ -3,6 +3,8 @@ div.menu-link {
|
||||
padding-left: 0.5ch;
|
||||
font-size: 1.1em;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
div.icon {
|
||||
@@ -26,6 +28,13 @@ div.menu-link {
|
||||
}
|
||||
}
|
||||
}
|
||||
div.menu-link:hover {
|
||||
div.text {
|
||||
span.weak {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
}
|
||||
div.menu-link.dark {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ body.theme-logs {
|
||||
color: #776;
|
||||
}
|
||||
.menu-link.selected {
|
||||
background-color: rgba(255, 187, 17, 0.125);
|
||||
background-color: rgba(255, 183, 0, 0.226);
|
||||
}
|
||||
.menu-link:hover, .Menu > .head-menu > a:hover {
|
||||
background-color: rgba(255, 187, 17, 0.25);
|
||||
@@ -37,13 +37,13 @@ body.theme-logs {
|
||||
color: #DA1;
|
||||
}
|
||||
.color-text, input, textarea, select {
|
||||
color: #AAA;
|
||||
color: #BBB;
|
||||
}
|
||||
.color-highlight-dark {
|
||||
background-color: rgba(18, 9, 0, 0.50);
|
||||
}
|
||||
.color-highlight-primary {
|
||||
background-color: rgba(255, 187, 15, 0.25);
|
||||
background-color: rgba(255, 187, 17, 0.25);
|
||||
color: #DA1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<menu user=input.user>
|
||||
<menu-header>Log</menu-header>
|
||||
<menu-link href=("/logs/?channels="+input.log.channel.name) icon="#">${input.log.channel.name}</menu-link>
|
||||
<menu-link if(input.log.channel.eventName) href=("/logs/?channels="+input.log.channel.eventName) icon="E">${input.log.channel.eventName}</menu-link>
|
||||
<for(character in input.log.characters)>
|
||||
<menu-link href=("/logs/?characters="+character.id) icon="C">${character.name} <span class="weak">(${character.author})</span></menu-link>
|
||||
</for>
|
||||
</menu>
|
||||
@@ -0,0 +1,17 @@
|
||||
const moment = require("moment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
log: input.log
|
||||
}
|
||||
}
|
||||
|
||||
get title() {
|
||||
if (this.state.log.title) {
|
||||
return this.state.log.title
|
||||
}
|
||||
|
||||
return `${this.state.log.channel.name} – ${moment(this.state.log.date).format("MMMM D, YYYY")}`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<logs-content-menu user=input.user log=state.log />
|
||||
<main>
|
||||
<div class="logs-content">
|
||||
<h1 class="color-primary">${component.title}</h1>
|
||||
<post for(post in state.log.posts) post=post characters=state.log.characters />
|
||||
</div>
|
||||
</main>
|
||||
@@ -0,0 +1,10 @@
|
||||
div.logs-content {
|
||||
width: 90%;
|
||||
max-width: 75ch;
|
||||
margin: auto;
|
||||
|
||||
h1 {
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
const moment = require("moment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {text: null, color: "color-menu", href: null, tooltip: null}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (this.state != null) {
|
||||
this.updateState(input)
|
||||
}
|
||||
}
|
||||
|
||||
updateState({kind, weak, value, updated}) {
|
||||
this.state = {text: null, color: "color-menu", href: null, tooltip: null}
|
||||
|
||||
if (value == null || value == "") {
|
||||
return
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case "timestamp": {
|
||||
const m = moment(value)
|
||||
if (m.year() < 2) {
|
||||
break
|
||||
}
|
||||
|
||||
this.state.tooltip = m.format("YYYY-MM-DD HH:mm:ss")
|
||||
this.state.text = `[${m.format("HH:mm")}]`
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "author": {
|
||||
this.state.text = value
|
||||
this.state.tooltip = "See all stories by " + value
|
||||
this.state.href = `/story/by-author/${value}/`
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
this.state.color = "color-menu"
|
||||
this.state.text = value
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<if(state.text != null && state.text != "")>
|
||||
<div class=["post-meta", input.kind, state.color, input.weak ? "weak" : null] title=state.tooltip>
|
||||
<a if(state.href != null) href=state.href>${state.text}</a>
|
||||
<span else>${state.text}</span>
|
||||
</div>
|
||||
</if>
|
||||
@@ -0,0 +1,20 @@
|
||||
div.post-meta {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
padding-right: 1em;
|
||||
margin: 0;
|
||||
|
||||
user-select: none;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
border-bottom: 0.25px dotted;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom: 0.5px solid;
|
||||
}
|
||||
}
|
||||
|
||||
div.post-meta.weak {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
shortName: "",
|
||||
name: "",
|
||||
text: "",
|
||||
}
|
||||
|
||||
this.updatePost(input)
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (this.state == null) {
|
||||
return
|
||||
}
|
||||
|
||||
this.updatePost(input)
|
||||
}
|
||||
|
||||
updatePost(input) {
|
||||
this.state.shortName = input.post.nick.split("_").shift()
|
||||
this.state.name = input.post.nick
|
||||
|
||||
this.state.text = input.post.text.replace(/\x02/g, "**")
|
||||
|
||||
if (input.post.kind === "text" && !this.state.text.includes("\"")) {
|
||||
this.state.text = '"' + this.state.text + '"'
|
||||
}
|
||||
|
||||
if (!input.post.nick.startsWith("=")) {
|
||||
const postNick = input.post.nick.replace("'s", "").replace("s'", "s")
|
||||
for (const character of input.characters) {
|
||||
for (const nick of character.nicks) {
|
||||
if (nick === postNick) {
|
||||
this.state.shortName = character.shortName
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kindClass(prefix) {
|
||||
if (this.input.post == null) {
|
||||
return
|
||||
}
|
||||
|
||||
return `${prefix}${this.input.post.kind.replace(".", "-")}`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class=["post", component.kindClass("post-type")]>
|
||||
<div class="post-meta-row">
|
||||
<post-meta kind="timestamp" value=input.post.time />
|
||||
<post-meta kind="nick" value=input.post.nick />
|
||||
</div>
|
||||
<div if(input.post.kind.startsWith("annotation.")) class="post-body" >
|
||||
<annotation level=(input.post.kind.substring(11))>
|
||||
<markdown class="post-content" source=state.text />
|
||||
</annotation>
|
||||
</div>
|
||||
<div if(input.post.kind === "scene") class="post-body color-text">
|
||||
<markdown class="post-content post-scene" source=state.text />
|
||||
</div>
|
||||
<div if(input.post.kind === "action") class="post-body color-text">
|
||||
<markdown class="post-content post-action" source=(state.shortName + " " + state.text) />
|
||||
</div>
|
||||
<div if(input.post.kind === "text") class="post-body color-text">
|
||||
<markdown class="post-content post-text" source=state.text />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
div.post {
|
||||
margin-bottom: 1ch;
|
||||
|
||||
.post-meta-row {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.post-body {
|
||||
font-size: 1.25em;
|
||||
|
||||
div.nick-wrapper {
|
||||
display: inline;
|
||||
|
||||
.nick-decoration {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.nick {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
div.post-content {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.annotation {
|
||||
margin-top: 0.33em;
|
||||
}
|
||||
}
|
||||
div.post-body p:first-of-type {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<include("../layout", {title: "Logs", site: "logs"})>
|
||||
<@body>
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<page log=input.log user=input.user selected=input.selected />
|
||||
</@body>
|
||||
</include>
|
||||
@@ -6,6 +6,7 @@ import moment from "moment"
|
||||
icon="L"
|
||||
link=("/logs/" + log.id)
|
||||
description=log.description
|
||||
characters=log.characters
|
||||
name=(log.title || (log.channelName + " - " + moment(log.date).format("MMMM D, YYYY")))
|
||||
tags=[{kind: "Location", name: log.channelName}, {kind: "Event", name: log.event}]
|
||||
enableAllTags=true
|
||||
|
||||
@@ -12,7 +12,7 @@ import moment from "moment"
|
||||
<chapter-meta weak kind="date" value=input.chapter.createdDate />
|
||||
<chapter-meta weak kind="author" value=input.chapter.author />
|
||||
</div>
|
||||
<markdown class="chapter-content" source=input.chapter.source />
|
||||
<markdown class="chapter-content color-text" source=input.chapter.source />
|
||||
|
||||
<edit-chapter-modal enabled=(state.modal === "edit") chapter=input.chapter on-close("close") on-edit("updateChapter") />
|
||||
<remove-chapter-modal enabled=(state.modal === "remove") chapter=input.chapter on-close("close") on-remove("removeChapter") />
|
||||
|
||||
@@ -31,4 +31,8 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div.chapter-content {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
const express = require("express")
|
||||
|
||||
const {logsApi} = require("../../rpdata/api/Log")
|
||||
|
||||
const viewTemplate = require("../../marko/page/logs-content/view.marko")
|
||||
|
||||
module.exports = async(req, res, next) => {
|
||||
res.markoAsync(viewTemplate, {
|
||||
log: logsApi.find(req.params.id),
|
||||
selected: {index: true},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
class Channel {
|
||||
constructor(name, logged, hub, eventName, locationName) {
|
||||
this.name = name
|
||||
this.logged = logged
|
||||
this.hub = hub
|
||||
this.eventName = eventName
|
||||
this.locationName = locationName
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new Channel(data.name, data.logged, data.hub, data.eventName, data.locationName)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Channel }
|
||||
+27
-2
@@ -1,5 +1,30 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
class Character {
|
||||
|
||||
}
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string[]} nicks
|
||||
* @param {string} author
|
||||
* @param {string} name
|
||||
* @param {string} shortName
|
||||
* @param {string} description
|
||||
*/
|
||||
constructor(id, nicks, author, name, shortName, description) {
|
||||
this.id = id
|
||||
this.nicks = nicks
|
||||
this.nick = this.nicks[0] || null
|
||||
this.author = author
|
||||
this.name = name
|
||||
this.shortName = shortName
|
||||
this.description = description
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a character object from data.
|
||||
*/
|
||||
static fromData(data) {
|
||||
return new Character(data.id, data.nicks, data.author, data.name, data.shortName, data.description)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Character }
|
||||
@@ -0,0 +1,81 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
const {Character} = require("./Character")
|
||||
const {Channel} = require("./Channel")
|
||||
const {Post} = require("./Post")
|
||||
|
||||
class Log {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} shortId
|
||||
* @param {Date | string | number} date
|
||||
* @param {string} title
|
||||
* @param {string} eventName
|
||||
* @param {string} description
|
||||
* @param {string} open
|
||||
* @param {any} channel
|
||||
* @param {any[]} characters
|
||||
* @param {any[]} posts
|
||||
*/
|
||||
constructor(id, shortId, date, title, eventName, description, open, channel, characters, posts) {
|
||||
this.id = id
|
||||
this.shortId = shortId
|
||||
this.date = date
|
||||
this.title = title
|
||||
this.eventName = eventName
|
||||
this.description = description
|
||||
this.open = open
|
||||
this.channel = Channel.fromData(channel)
|
||||
this.characters = characters.map(c => Character.fromData(c))
|
||||
this.posts = posts.map(p => Post.fromData(p))
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new Log(data.id, data.shortId, data.date, data.title, data.eventName, data.description, data.open, data.channel, data.characters, data.posts)
|
||||
}
|
||||
}
|
||||
|
||||
const logsApi = {
|
||||
find(id) {
|
||||
return query(`
|
||||
query FindLog($id: String!) {
|
||||
log(id: $id) {
|
||||
id
|
||||
shortId
|
||||
date
|
||||
title
|
||||
eventName
|
||||
description
|
||||
open
|
||||
channel {
|
||||
name
|
||||
logged
|
||||
hub
|
||||
eventName
|
||||
locationName
|
||||
}
|
||||
characters {
|
||||
id
|
||||
nicks
|
||||
author
|
||||
name
|
||||
shortName
|
||||
description
|
||||
}
|
||||
posts {
|
||||
id
|
||||
position
|
||||
time
|
||||
kind
|
||||
nick
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {id}).then(({log}) => {
|
||||
return Log.fromData(log)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = { Log, logsApi }
|
||||
@@ -0,0 +1,26 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
class Post {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {number} position
|
||||
* @param {Date | string | number} time
|
||||
* @param {string} kind
|
||||
* @param {string} nick
|
||||
* @param {string} text
|
||||
*/
|
||||
constructor(id, position, time, kind, nick, text) {
|
||||
this.id = id
|
||||
this.position = position
|
||||
this.time = new Date(time)
|
||||
this.kind = kind
|
||||
this.nick = nick
|
||||
this.text = text
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new Post(data.id, data.position, data.time, data.kind, data.nick, data.text)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Post}
|
||||
@@ -76,7 +76,8 @@ app.use("/story/by-tag/", require("./routes/story/by-tag"))
|
||||
app.use("/story/tag-list/", require("./routes/story/tag-list"))
|
||||
app.use("/story/:id(S[0-9a-z]{15})/", require("./routes/story-content"))
|
||||
app.use("/logs/", require("./routes/logs"))
|
||||
|
||||
app.use("/logs/:id([0-9]{4}-[0-1][0-9]-[0-3][0-9]_[0-9]{9}_[A-Za-z\,\']{2,})/", require("./routes/logs-content"))
|
||||
// 2018-03-10_035344747_Miner'sRespite
|
||||
// Entry point
|
||||
app.get("/", function(req, res) {
|
||||
res.redirect("/story/")
|
||||
|
||||
Reference in New Issue
Block a user