Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c55fc03aa | ||
|
|
6fca626ea2 | ||
|
|
d92e6bfeb0 | ||
|
|
2d89096430 | ||
|
|
3988028256 | ||
|
|
c51733ef3b | ||
|
|
f81f05d274 | ||
|
|
25d4aa73d1 | ||
|
|
e1a5968341 | ||
|
|
5c4446f161 | ||
|
|
3a057f6f24 | ||
|
|
853076fd5e | ||
|
|
06bf2b67a2 | ||
|
|
40259b8839 | ||
|
|
3fc9d5de7f | ||
|
|
c69e528105 | ||
|
|
6ea9632623 | ||
|
|
490725ecd2 | ||
|
|
494d0404ed | ||
|
|
8bfc0dad14 | ||
|
|
3ed3e50486 | ||
|
|
5260d96f37 |
@@ -0,0 +1,5 @@
|
||||
import moment from "moment";
|
||||
|
||||
<if(input.value != null)>
|
||||
<span class=["date", input.class]>${(input.utc ? moment.utc(input.value) : moment(input.value)).format(input.format || "MMM D, YYYY")}</span>
|
||||
</if>
|
||||
@@ -0,0 +1,23 @@
|
||||
const {parse, BOLD} = require("irc-caret-notation")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
paragraphs: [],
|
||||
prev: "",
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (this.state == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (input.source != this.state.prev) {
|
||||
this.state.prev = input.source
|
||||
this.state.paragraphs = input.source.split("\n").filter(l => l.length > 0).map(l => parse(l, {
|
||||
"*": (!(input.source.startsWith("(")) ? BOLD : null),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class=["irc-caret-notation", input.class]>
|
||||
<p for(p in state.paragraphs)>
|
||||
<span for(chunk in p) class=chunk.cssClasses("icn-")>${chunk.text}</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -1,3 +1,4 @@
|
||||
<div class=["markdown-content", input.class]>
|
||||
<include(input.renderBody) />
|
||||
<include(state.render) />
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<a on-click("onClick") href=input.href>
|
||||
<div class=state.classes>
|
||||
<div class="icon color-primary">${input.icon}</div>
|
||||
<div class=[state.classes, input.weak ? "dark" : null]>
|
||||
<div class=["icon", "color-primary"]>${input.icon}</div>
|
||||
<div class=["text", input.selected ? "color-primary" : input.textClass]>
|
||||
<include(input.renderBody) />
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,9 @@ module.exports = class {
|
||||
if (this.input.enabled) {
|
||||
window.removeEventListener("keydown", this.onPress)
|
||||
}
|
||||
|
||||
this.mounted = false
|
||||
this.opened = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,3 +51,71 @@ body {
|
||||
background-color: #555;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tooltip magic.
|
||||
*/
|
||||
.tooltip {
|
||||
position: relative;
|
||||
border-bottom: 0.5px dotted; /* If you want dots under the hoverable text */
|
||||
|
||||
span {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
> .tooltip-content {
|
||||
visibility: hidden;
|
||||
width: 400px;
|
||||
text-align: left;
|
||||
padding: 0.25em 1ch;
|
||||
|
||||
position: absolute;
|
||||
z-index: 11000;
|
||||
pointer-events: none;
|
||||
|
||||
top: 0;
|
||||
left: 105%;
|
||||
|
||||
border: 1px solid;
|
||||
|
||||
> h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
|
||||
> h2 {
|
||||
font-size: 1em;
|
||||
margin: 0;
|
||||
margin-bottom: 0.15em;
|
||||
margin-top: -0.25em;
|
||||
font-weight: 200;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
|
||||
p:last-of-type {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip:hover {
|
||||
> .tooltip-content {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.tooltip {
|
||||
> .tooltip-content {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ body.theme-data {
|
||||
}
|
||||
|
||||
|
||||
div.markdown-content .orange, body.theme-logs div.markdown-content blockquote, body.theme-logs div.markdown-content table, body.theme-logs div.markdown-content pre {
|
||||
div.markdown-content .orange, body.theme-logs .color-blockquote, body.theme-logs div.markdown-content blockquote, body.theme-logs div.markdown-content table, body.theme-logs div.markdown-content pre {
|
||||
background-color: rgba(221, 170, 17, 0.125);
|
||||
color: #DA1;
|
||||
|
||||
@@ -100,7 +100,7 @@ div.markdown-content .orange, body.theme-logs div.markdown-content blockquote, b
|
||||
color: #DA1;
|
||||
}
|
||||
}
|
||||
div.markdown-content .blue, body.theme-story div.markdown-content blockquote, body.theme-story div.markdown-content table, body.theme-story div.markdown-content pre {
|
||||
div.markdown-content .blue, body.theme-story .color-blockquote, body.theme-story div.markdown-content blockquote, body.theme-story div.markdown-content table, body.theme-story div.markdown-content pre {
|
||||
background-color: rgba(34, 187, 255, 0.125);
|
||||
color: #4BF;
|
||||
|
||||
@@ -108,7 +108,7 @@ div.markdown-content .blue, body.theme-story div.markdown-content blockquote, bo
|
||||
color: #4BF; //
|
||||
}
|
||||
}
|
||||
div.markdown-content .green, body.theme-mapp div.markdown-content blockquote, body.theme-mapp div.markdown-content table, body.theme-mapp div.markdown-content pre {
|
||||
div.markdown-content .green, body.theme-mapp .color-blockquote, body.theme-mapp div.markdown-content blockquote, body.theme-mapp div.markdown-content table, body.theme-mapp div.markdown-content pre {
|
||||
background-color: rgba(17,255,136, 0.125);
|
||||
color: #1F8;
|
||||
|
||||
|
||||
@@ -72,6 +72,21 @@ module.exports = class {
|
||||
break
|
||||
}
|
||||
|
||||
case "Comment": {
|
||||
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") || {}
|
||||
const comment = data.objects.find(o => o.type === "Comment") || {}
|
||||
|
||||
this.state.operation += ' ' + comment.characterName + " comment in chapter"
|
||||
this.state.links = [{
|
||||
text: chapter.title || `ID (${chapterKey.id})`,
|
||||
href: `/story/${storyKey.id}#${chapterKey.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Log": {
|
||||
const key = data.keys.find(k => k.id !== "*")
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
highlights: [],
|
||||
}
|
||||
}
|
||||
|
||||
select(value) {
|
||||
this.emit("select", value)
|
||||
}
|
||||
|
||||
toggleHighlight(id) {
|
||||
if (this.state.highlights.includes(id)) {
|
||||
this.state.highlights = this.state.highlights.filter(id2 => id2 !== id)
|
||||
} else {
|
||||
this.state.highlights = this.state.highlights.concat(id)
|
||||
}
|
||||
|
||||
this.emit("hightlights", this.state.highlights);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,23 @@
|
||||
<menu-link dark key="_create" on-click("select", "post.add", null) icon="+">Add Post</menu-link>
|
||||
<menu-gap />
|
||||
</if-permitted>
|
||||
<menu-header>Links</menu-header>
|
||||
<menu-header>Channel</menu-header>
|
||||
<menu-link href=("/logs/?channels="+encodeURIComponent(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-gap />
|
||||
<if(input.log.eventName)>
|
||||
<menu-header>Event</menu-header>
|
||||
<menu-link href=("/logs/?events="+input.log.eventName) icon="E">${input.log.eventName}</menu-link>
|
||||
<menu-gap />
|
||||
</if>
|
||||
<if(input.log.characters.length > 0)>
|
||||
<menu-header>Characters</menu-header>
|
||||
<for(character in input.log.characters)>
|
||||
<menu-link weak=(state.highlights.length > 0 && !state.highlights.includes(character.id))
|
||||
icon="C"
|
||||
on-click("toggleHighlight", character.id)>
|
||||
${character.name} <span class="weak">(${character.author})</span>
|
||||
</menu-link>
|
||||
</for>
|
||||
<menu-gap />
|
||||
</if>
|
||||
</menu>
|
||||
@@ -9,6 +9,7 @@ module.exports = class {
|
||||
log: input.log,
|
||||
modal: null,
|
||||
removed: false,
|
||||
highlights: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +112,11 @@ module.exports = class {
|
||||
}
|
||||
|
||||
postAdded(post) {
|
||||
// Stop duplicates in case of race condition.
|
||||
if (this.state.log.posts.find(p => p.id === post.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.state.log.posts = this.state.log.posts.concat([post])
|
||||
this.state.log = Object.assign({}, this.state.log)
|
||||
}
|
||||
@@ -161,6 +167,10 @@ module.exports = class {
|
||||
this.state.removed = true
|
||||
}
|
||||
|
||||
highlightsChanged(highlights) {
|
||||
this.state.highlights = highlights.slice();
|
||||
}
|
||||
|
||||
get title() {
|
||||
if (this.state.log.title) {
|
||||
return this.state.log.title
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<logs-content-menu user=input.user log=state.log on-select("open") />
|
||||
<logs-content-menu on-hightlights("highlightsChanged") user=input.user log=state.log on-select("open") />
|
||||
<main>
|
||||
<div class="logs-content">
|
||||
<div class="header">
|
||||
@@ -25,6 +25,7 @@
|
||||
prev=(state.log.posts[loop.getIndex() - 1] || {})
|
||||
post=post
|
||||
characters=state.log.characters
|
||||
highlights=state.highlights
|
||||
last=(post.position === state.log.posts.length)
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ module.exports = class {
|
||||
multipart: false,
|
||||
anchored: false,
|
||||
prevWasText: false,
|
||||
dark: false,
|
||||
}
|
||||
|
||||
this.mounted = false;
|
||||
@@ -48,7 +49,7 @@ module.exports = class {
|
||||
}
|
||||
|
||||
const timediff = Date.parse(input.post.time) - Date.parse(input.prev.time)
|
||||
this.state.multipart = (input.post.nick == input.prev.nick && input.prev.multipart !== false) && (Math.abs(timediff) < 60000);
|
||||
this.state.multipart = (!input.post.kind.startsWith("annotation.") && input.post.nick == input.prev.nick && input.prev.multipart !== false) && (Math.abs(timediff) < 60000);
|
||||
this.state.prevWasText = (input.prev.kind === "text");
|
||||
|
||||
this.updatePost(input)
|
||||
@@ -80,25 +81,13 @@ module.exports = class {
|
||||
this.state.shortName = input.post.nick.split("_").shift()
|
||||
this.state.name = input.post.nick
|
||||
this.state.nameSuffix = ""
|
||||
|
||||
this.state.text = input.post.text.replace(/\x02/g, "**").replace(/\x1D/g, "*").replace(/\`/g, "\\`").trim();
|
||||
|
||||
if (this.state.text.startsWith("|")) {
|
||||
this.state.name = ""
|
||||
this.state.text = this.state.text.slice(1).trim();
|
||||
}
|
||||
this.state.text = input.post.text
|
||||
|
||||
if (input.post.kind === "text" && !this.state.text.includes("\"") && !this.state.text.includes("\''") && !this.state.text.includes("|")) {
|
||||
const colonIndex = this.state.text.indexOf(": ");
|
||||
if (colonIndex != -1 && colonIndex < this.state.text.indexOf(" ")) {
|
||||
this.state.text = this.state.text.replace(": ", ": \"") + "\"";
|
||||
} else {
|
||||
this.state.text = '"' + this.state.text + '"'
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.text.charAt(0) == this.state.text.charAt(0).toUpperCase()) {
|
||||
this.state.name = ""
|
||||
if (input.highlights.length > 0 && (input.post.kind === "action" || input.post.kind === "text")) {
|
||||
this.state.dark = true
|
||||
} else {
|
||||
this.state.dark = false
|
||||
}
|
||||
|
||||
if (!input.post.nick.startsWith("=")) {
|
||||
@@ -106,13 +95,19 @@ module.exports = class {
|
||||
for (const character of input.characters) {
|
||||
for (const nick of character.nicks) {
|
||||
if (nick === postNick) {
|
||||
this.state.name = character.name
|
||||
this.state.name = character.name + character.id
|
||||
this.state.shortName = character.shortName
|
||||
|
||||
if (input.post.nick.endsWith("'s") || input.post.nick.endsWith("'")) {
|
||||
this.state.nameSuffix = (character.shortName.endsWith("s") || character.shortName.endsWith("z")) ? "'" : "'s"
|
||||
}
|
||||
|
||||
if (input.highlights.length > 0) {
|
||||
this.state.dark = !(input.highlights.includes(character.id))
|
||||
} else {
|
||||
this.state.dark = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div if(!state.removed) class=["post", component.kindClass("pk-"), state.multipart ? "multipart" : null, state.anchored ? "anchored" : null]>
|
||||
<div if(!state.removed) class=["post", component.kindClass("pk-"), state.multipart ? "multipart" : null, state.anchored ? "anchored" : null, state.dark ? "dark" : null]>
|
||||
<a class="anchor" id=input.post.id />
|
||||
<div class="post-meta-row">
|
||||
<div class="options color-menu">
|
||||
@@ -19,18 +19,18 @@
|
||||
</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 />
|
||||
<irc-caret-notation 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 />
|
||||
<irc-caret-notation class="post-content post-scene" source=state.text />
|
||||
</div>
|
||||
<div if(input.post.kind === "action") class="post-body color-text">
|
||||
$ const prefix = ((!state.multipart || state.prevWasText) ? state.shortName + state.nameSuffix + " " : "");
|
||||
<markdown class="post-content post-action" source=(prefix + state.text) />
|
||||
<irc-caret-notation class="post-content post-action" source=(prefix + state.text) />
|
||||
</div>
|
||||
<div if(input.post.kind === "text") class="post-body color-text">
|
||||
<markdown class="post-content post-text" source=state.text />
|
||||
<div if(input.post.kind === "text") class="post-body color-text">
|
||||
<irc-caret-notation class="post-content post-text" source=state.text />
|
||||
</div>
|
||||
<remove-post-modal enabled=(state.modal === "remove") post=input.post on-close("close") on-remove("remove") />
|
||||
<edit-post-modal enabled=(state.modal === "edit") post=input.post on-close("close") on-edited("edited") />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
div.post {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1ch;
|
||||
margin-bottom: 1.25em;
|
||||
margin-left: -12px;
|
||||
padding-left: 12px;
|
||||
padding-right: 0.5ch;
|
||||
@@ -65,12 +65,54 @@ div.post {
|
||||
margin-top: 0.33em;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.33em;
|
||||
margin-bottom: -0.25em;
|
||||
|
||||
span.icn-bold { font-weight: 800; }
|
||||
span.icn-underline { text-decoration: underline; }
|
||||
span.icn-italic { font-style: italic; }
|
||||
|
||||
span.icn-fg-0 { color: #fff; }
|
||||
span.icn-fg-1 { color: #111; }
|
||||
span.icn-fg-2 { color: #13d; }
|
||||
span.icn-fg-3 { color: #173; }
|
||||
span.icn-fg-4 { color: #d21; }
|
||||
span.icn-fg-5 { color: #721; }
|
||||
span.icn-fg-6 { color: #727; }
|
||||
span.icn-fg-7 { color: #d71; }
|
||||
span.icn-fg-8 { color: #aa1; }
|
||||
span.icn-fg-9 { color: #1d3; }
|
||||
span.icn-fg-10 { color: #177; }
|
||||
span.icn-fg-11 { color: #1DD; }
|
||||
span.icn-fg-12 { color: #127; }
|
||||
span.icn-fg-13 { color: #c1a; }
|
||||
span.icn-fg-14 { color: #444; }
|
||||
span.icn-fg-15 { color: #aaa; }
|
||||
span.icn-bg-0 { background-color: #fff; }
|
||||
span.icn-bg-1 { background-color: #111; }
|
||||
span.icn-bg-2 { background-color: #13d; }
|
||||
span.icn-bg-3 { background-color: #173; }
|
||||
span.icn-bg-4 { background-color: #d21; }
|
||||
span.icn-bg-5 { background-color: #721; }
|
||||
span.icn-bg-6 { background-color: #727; }
|
||||
span.icn-bg-7 { background-color: #d71; }
|
||||
span.icn-bg-8 { background-color: #aa1; }
|
||||
span.icn-bg-9 { background-color: #1d3; }
|
||||
span.icn-bg-10 { background-color: #177; }
|
||||
span.icn-bg-11 { background-color: #1DD; }
|
||||
span.icn-bg-12 { background-color: #127; }
|
||||
span.icn-bg-13 { background-color: #c1a; }
|
||||
span.icn-bg-14 { background-color: #444; }
|
||||
span.icn-bg-15 { background-color: #aaa; }
|
||||
}
|
||||
p:first-of-type {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
p:last-of-type {
|
||||
padding-bottom: 0.1em;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,11 +133,15 @@ div.post.pk-text.anchored {
|
||||
}
|
||||
|
||||
div.post.multipart {
|
||||
margin-top: -0.9em;
|
||||
margin-top: -1.5em;
|
||||
|
||||
div.post-meta {
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.post.dark {
|
||||
opacity: 0.25;
|
||||
}
|
||||
@@ -50,6 +50,13 @@ module.exports = class {
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "title": {
|
||||
this.state.color = "color-primary"
|
||||
this.state.text = value
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
this.state.color = "color-menu"
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
<if(state.text != null && state.text != "")>
|
||||
<div class=["chapter-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>
|
||||
<span else class="tooltip">
|
||||
<span>${state.text}</span>
|
||||
|
||||
<div if(input.character != null) class="tooltip-content color-highlight-dark color-primary">
|
||||
<h1>${input.character.name}</h1>
|
||||
<h2>${input.character.author}</h2>
|
||||
<markdown source=input.character.description />
|
||||
</div>
|
||||
<div else-if(input.ukAuthor != null) class="tooltip-content color-highlight-dark color-primary">
|
||||
<h1>(Unknown Character)</h1>
|
||||
<h2>${input.ukAuthor}</h2>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</if>
|
||||
@@ -13,6 +13,10 @@ div.chapter-meta {
|
||||
a:hover {
|
||||
border-bottom: 0.5px solid;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
border-bottom: 0.25px dotted;
|
||||
}
|
||||
}
|
||||
|
||||
div.chapter-meta.weak {
|
||||
|
||||
@@ -3,9 +3,42 @@ module.exports = class {
|
||||
this.state = {
|
||||
modal: null,
|
||||
removed: false,
|
||||
commentLabels: {
|
||||
Article: "Comment",
|
||||
Message: "Message",
|
||||
Chat: "Chat Message",
|
||||
},
|
||||
selectedComment: null,
|
||||
}
|
||||
}
|
||||
|
||||
onOpenComment(comment, modal) {
|
||||
this.state.selectedComment = comment
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
addComment(comment) {
|
||||
this.updateChapter({
|
||||
comments: this.input.chapter.comments.concat(comment),
|
||||
})
|
||||
}
|
||||
|
||||
editComment(comment) {
|
||||
const comments = this.input.chapter.comments.slice()
|
||||
const index = comments.findIndex(c => c.id === comment.id)
|
||||
if (index != -1) {
|
||||
comments[index] = comment
|
||||
} else {
|
||||
comments.push(comment)
|
||||
}
|
||||
|
||||
this.updateChapter({comments})
|
||||
}
|
||||
|
||||
removeComment(comment) {
|
||||
this.updateChapter({comments: this.input.chapter.comments.filter(c => c.id !== comment.id)})
|
||||
}
|
||||
|
||||
updateChapter(data) {
|
||||
this.emit("edit", data)
|
||||
}
|
||||
|
||||
@@ -16,10 +16,26 @@
|
||||
<chapter-meta kind="date" utc=true value=input.chapter.fictionalDate />
|
||||
<chapter-meta weak kind="date" value=input.chapter.createdDate />
|
||||
<chapter-meta weak kind="author" value=input.chapter.author />
|
||||
<chapter-meta weak kind="message" value=input.chapter.commentMode />
|
||||
</div>
|
||||
<markdown class="chapter-content color-text" source=input.chapter.source />
|
||||
<div class=["comment-section", "type-" + (input.chapter.commentMode.toLowerCase() || "disabled")]>
|
||||
<comment for(comment in input.chapter.comments) key=comment.id
|
||||
comment=comment
|
||||
locked=input.chapter.commentsLocked
|
||||
mode=input.chapter.commentMode
|
||||
user=input.user
|
||||
on-open("onOpenComment", comment)
|
||||
/>
|
||||
|
||||
<if-permitted if(input.chapter.canComment) user=input.user permission=["member", "chapter.edit"]>
|
||||
<div class="add-comment color-menu" on-click("open", "addComment") >Add ${state.commentLabels[input.chapter.commentMode] || "Comment"}</div>
|
||||
</if-permitted>
|
||||
</div>
|
||||
<edit-chapter-modal enabled=(state.modal === "edit") chapter=input.chapter on-close("close") on-edit("updateChapter") />
|
||||
<create-comment-modal enabled=(state.modal === "addComment") chapter=input.chapter user=input.user on-close("close") on-add("addComment") />
|
||||
<edit-comment-modal enabled=(state.modal === "editComment") chapter=input.chapter comment=state.selectedComment user=input.user on-close("close") on-edit("editComment") />
|
||||
<remove-comment-modal enabled=(state.modal === "removeComment") comment=state.selectedComment user=input.user on-close("close") on-remove("removeComment", state.selectedComment) />
|
||||
<move-chapter-modal enabled=(state.modal === "move") chapter=input.chapter user=input.user on-close("close") on-move("moveChapter") />
|
||||
<remove-chapter-modal enabled=(state.modal === "remove") chapter=input.chapter on-close("close") on-remove("removeChapter") />
|
||||
</div>
|
||||
@@ -49,4 +49,30 @@
|
||||
div.chapter-content {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
div.comment-section {
|
||||
margin: 1em 4ch 2em 4ch;
|
||||
|
||||
div.add-comment {
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
padding: 1em 0;
|
||||
font-size: 1.5em;
|
||||
border: 1px solid;
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.add-comment:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
div.add-comment:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
div.comment-section {
|
||||
margin: 1em 2ch 2em 2ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
showCharacter: false,
|
||||
paragraphs: input.comment.source.split('\n').filter(l => l.length > 1),
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (this.state) {
|
||||
this.state.paragraphs = input.comment.source.split('\n').filter(l => l.length > 1)
|
||||
}
|
||||
}
|
||||
|
||||
toggle(key) {
|
||||
this.state[key] = !this.state[key]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import moment from "moment"
|
||||
|
||||
<div class=["comment", "mode-" + input.mode.toLowerCase()]>
|
||||
<if (input.mode === "Message")>
|
||||
<div class="metadata">
|
||||
<div class="options color-menu">
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
|
||||
</if-permitted>
|
||||
</div>
|
||||
<chapter-meta weak kind="date" value=input.comment.createdDate />
|
||||
<chapter-meta weak kind="author" value=input.comment.author />
|
||||
</div>
|
||||
<div class="content color-blockquote">
|
||||
<table class="header">
|
||||
<tbody>
|
||||
<tr if(input.comment.subject != "")>
|
||||
<th>Subject:</th>
|
||||
<td>${input.comment.subject}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>From:</th>
|
||||
<td>
|
||||
<div if(input.comment.character != null) class="tooltip" style="display: inline-block">
|
||||
<span>${input.comment.characterName}</span>
|
||||
|
||||
<div class="tooltip-content color-highlight-dark">
|
||||
<h1>${input.comment.character.name}</h1>
|
||||
<h2>${input.comment.character.author}</h2>
|
||||
<markdown source=input.comment.character.description />
|
||||
</div>
|
||||
</div>
|
||||
<div else>
|
||||
<span class="tooltip color-primary">
|
||||
<span>${input.comment.characterName}</span>
|
||||
|
||||
<div class="tooltip-content color-highlight-dark color-primary">
|
||||
<h1>(Unknown Character)</h1>
|
||||
<h2>${input.comment.author}</h2>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr if(input.comment.fictionalDate != null)>
|
||||
<th>Date:</th>
|
||||
<td>
|
||||
<date utc value=input.comment.fictionalDate></date>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<markdown class="body" source=input.comment.source />
|
||||
</div>
|
||||
</if>
|
||||
<if(input.mode === "Chat")>
|
||||
<div class="content">
|
||||
<div class="options color-menu">
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
|
||||
</if-permitted>
|
||||
</div>
|
||||
<for (paragraph in state.paragraphs)>
|
||||
<div class="message-text color-text">
|
||||
<markdown class="body" source=paragraph>
|
||||
<if (input.comment.fictionalDate)>
|
||||
<span class="decoration color-menu">[</span>
|
||||
<date utc class="color-menu" value=input.comment.fictionalDate></date>
|
||||
<span class="decoration color-menu">] </span>
|
||||
</if>
|
||||
<span class="decoration color-menu"><</span>
|
||||
<span if(input.comment.character != null) class="tooltip color-primary">
|
||||
<span>${input.comment.characterName}</span>
|
||||
|
||||
<div class="tooltip-content color-highlight-dark color-primary">
|
||||
<h1>${input.comment.character.name}</h1>
|
||||
<h2>${input.comment.character.author}</h2>
|
||||
<markdown source=input.comment.character.description />
|
||||
</div>
|
||||
</span>
|
||||
<span class="tooltip color-primary" else>
|
||||
<span>${input.comment.characterName}</span>
|
||||
|
||||
<div class="tooltip-content color-highlight-dark color-primary">
|
||||
<h1>(Unknown Character)</h1>
|
||||
<h2>${input.comment.author}</h2>
|
||||
</div>
|
||||
</span>
|
||||
<span class="decoration color-menu">> </span>
|
||||
</markdown>
|
||||
</div>
|
||||
</for>
|
||||
</div>
|
||||
</if>
|
||||
<if(input.mode === "Article")>
|
||||
<div class="metadata">
|
||||
<div class="options color-menu">
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=input.comment.author permission="story.edit">
|
||||
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
|
||||
</if-permitted>
|
||||
</div>
|
||||
<chapter-meta kind="title" value=input.comment.subject />
|
||||
<chapter-meta kind="name" value=input.comment.characterName character=(input.comment.character) ukAuthor=input.comment.author />
|
||||
<chapter-meta utc kind="date" value=input.comment.fictionalDate />
|
||||
<chapter-meta weak kind="date" value=input.comment.createdDate />
|
||||
<chapter-meta weak kind="author" value=input.comment.author />
|
||||
</div>
|
||||
<div class="content color-text">
|
||||
<markdown class="body" source=input.comment.source />
|
||||
</div>
|
||||
</if>
|
||||
</div>
|
||||
@@ -0,0 +1,113 @@
|
||||
div.comment {
|
||||
position: relative;
|
||||
|
||||
div.options {
|
||||
width: 100%;
|
||||
max-width: 95ch;
|
||||
}
|
||||
}
|
||||
|
||||
div.comment.mode-message {
|
||||
margin-bottom: 1.5em;
|
||||
font-size: 1em;
|
||||
|
||||
.metadata {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.content {
|
||||
outline: 1px solid;
|
||||
padding: 0.25em 1ch;
|
||||
|
||||
font-size: 1.25em;
|
||||
|
||||
table.header {
|
||||
border-bottom: 0.5px solid;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
text-align: left;
|
||||
|
||||
th {
|
||||
width: 12ch;
|
||||
padding-right: 1ch;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.body {
|
||||
padding-top: 0.5em;
|
||||
|
||||
p:first-of-type {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.comment.mode-chat {
|
||||
div.options {
|
||||
display: none;
|
||||
bottom: -0.25em;
|
||||
|
||||
a {
|
||||
background: black;
|
||||
opacity: 0.666;
|
||||
}
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div.content {
|
||||
line-height: 2em;
|
||||
font-size: 1.125em;
|
||||
|
||||
div.tooltip-content {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
div.message-text {
|
||||
vertical-align: top;
|
||||
padding-left: 2ch;
|
||||
text-indent: -2ch;
|
||||
|
||||
div.body {
|
||||
p:first-of-type {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.comment.mode-article {
|
||||
margin-bottom: 1em;
|
||||
|
||||
.content {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
div.comment.mode-chat:hover {
|
||||
div.options {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ module.exports = class {
|
||||
title: "",
|
||||
source: "",
|
||||
fictionalDate: "",
|
||||
commentMode: "Disabled",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,18 +20,18 @@ module.exports = class {
|
||||
|
||||
onInput(input) {
|
||||
if (input.chapter && !this.first) {
|
||||
let {fictionalDate, title, source} = input.chapter
|
||||
let {fictionalDate, title, source, commentMode, commentsLocked} = input.chapter
|
||||
|
||||
if (fictionalDate != null) {
|
||||
fictionalDate = moment.utc(fictionalDate).format("MMM D, YYYY")
|
||||
}
|
||||
|
||||
this.state.values = {fictionalDate, title, source}
|
||||
this.state.values = {fictionalDate, title, source, commentMode, commentsLocked}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values[key] = ev.target.value
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
@@ -52,15 +53,19 @@ module.exports = class {
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date!`
|
||||
if (Number.isNaN(fictionalDate.getTime())) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date.`
|
||||
if (values.fictionalDate.includes("th")) {
|
||||
this.state.error += " Try to remove the 'th'."
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fictionalDate = null
|
||||
}
|
||||
|
||||
const input = {storyId: this.input.storyId, title: values.title, source: values.source, fictionalDate}
|
||||
const input = {storyId: this.input.storyId, title: values.title, source: values.source, fictionalDate, commentMode: values.commentMode}
|
||||
|
||||
chapterApi.addChapter(input).then(chapter => {
|
||||
this.emit("add", chapter)
|
||||
@@ -70,11 +75,12 @@ module.exports = class {
|
||||
title: "",
|
||||
source: "",
|
||||
fictionalDate: "",
|
||||
commentMode: "Disabled",
|
||||
}
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to edit:", errs)
|
||||
|
||||
this.state.error = "Failed to edit: " + errs[0].message
|
||||
this.state.error = "Failed to edit: " + (errs[0]||errs||{}).message || errs
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
|
||||
@@ -12,5 +12,13 @@
|
||||
<label>Content</label>
|
||||
<textarea key="source" placeholder="" class="tall" on-change("change", "source") value=state.values.source />
|
||||
|
||||
<label>Comments</label>
|
||||
<select key="kind" class="big" placeholder="Kind" on-change("change", "commentMode") value=state.values.commentMode>
|
||||
<option value="Disabled" selected=(state.values.commentMode === "Disabled")>Disabled – Hide all comments</option>
|
||||
<option value="Article" selected=(state.values.commentMode === "Article")>Article – Looks like story chapters</option>
|
||||
<option value="Chat" selected=(state.values.commentMode === "Chat")>Chat – One-line chat messages</option>
|
||||
<option value="Message" selected=(state.values.commentMode === "Message")>Message – Extranet Mail</option>
|
||||
</select>
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,139 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {commentApi} = require("../../../../../rpdata/api/Comment")
|
||||
const {charactersApi} = require("../../../../../rpdata/api/Character")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
subject: "",
|
||||
source: "",
|
||||
characterName: "",
|
||||
characterId: "",
|
||||
fictionalDate: "",
|
||||
},
|
||||
characters: [],
|
||||
opened: false,
|
||||
placeholder: "(Required)",
|
||||
}
|
||||
|
||||
this.first = false
|
||||
}
|
||||
|
||||
onMount() {
|
||||
charactersApi.list({author: (this.input.user||{}).name}).then((characters) => {
|
||||
this.state.characters = characters.sort((a,b) => a.name.localeCompare(b.name))
|
||||
})
|
||||
|
||||
const comments = this.input.chapter.comments || []
|
||||
const lastComment = comments[comments.length - 1]
|
||||
if (lastComment != null) {
|
||||
let date = lastComment.fictionalDate || this.input.chapter.fictionalDate
|
||||
if (date != null) {
|
||||
this.change("fictionalDate", {target: {value: moment(date).format("MMM D, YYYY")}})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
|
||||
if (key === "characterId") {
|
||||
if (ev.target.value !== "") {
|
||||
const character = this.state.characters.find(c => c.id === this.state.values.characterId)
|
||||
if (character != null) {
|
||||
this.state.placeholder = character.name
|
||||
} else {
|
||||
this.state.placeholder = "(Required)"
|
||||
}
|
||||
} else {
|
||||
this.state.placeholder = "(Required)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
if (!this.state.opened) {
|
||||
charactersApi.list({author: (this.input.user||{}).name}).then((characters) => {
|
||||
this.state.characters = characters.sort((a,b) => a.name.localeCompare(b.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.first = false
|
||||
this.state.opened = false
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
const values = Object.assign({}, this.state.values)
|
||||
|
||||
if (values.source.length == 0) {
|
||||
this.state.error = "The comment body is empty."
|
||||
console.warn(values)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.input.chapter.commentMode !== "Chat" && values.subject.trim().length == 0) {
|
||||
this.state.error = "The subject is empty."
|
||||
return
|
||||
}
|
||||
|
||||
if (values.characterName.trim().length < 1) {
|
||||
const character = this.state.characters.find(c => c.id === this.state.values.characterId)
|
||||
if (character == null) {
|
||||
this.state.error = "You need to give a display name for an anonymous character."
|
||||
return
|
||||
}
|
||||
|
||||
values.characterName = character.name
|
||||
}
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date!`
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fictionalDate = null
|
||||
}
|
||||
|
||||
const input = {
|
||||
chapterId: this.input.chapter.id,
|
||||
subject: values.subject,
|
||||
source: values.source,
|
||||
characterName: values.characterName,
|
||||
characterId: values.characterId,
|
||||
fictionalDate: fictionalDate,
|
||||
}
|
||||
|
||||
if (input.characterId == "") {
|
||||
input.characterId = null
|
||||
}
|
||||
|
||||
commentApi.addComment(input).then(comment => {
|
||||
this.emit("add", comment)
|
||||
this.emit("close")
|
||||
|
||||
this.state.values = {
|
||||
subject: "",
|
||||
source: "",
|
||||
characterName: "",
|
||||
characterId: "",
|
||||
fictionalDate: "",
|
||||
}
|
||||
this.state.opened = false
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add comemnt:", errs)
|
||||
|
||||
this.state.error = "Failed to add comemnt: " + (errs[0]||errs||{}).message || errs
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Add Comment</h1>
|
||||
|
||||
<p key="error" class="color-error">${state.error}</p>
|
||||
|
||||
<if(input.chapter.commentMode !== "Chat")>
|
||||
<label>Subject</label>
|
||||
<input key="subject" autofocus placeholder="" class="big" on-change("change", "subject") value=state.values.subject />
|
||||
</if>
|
||||
|
||||
<label>Character ${state.characterId}</label>
|
||||
<select on-change("change", "characterId")>
|
||||
<option value="" selected=state.values.characterId=="">Anonymous</option>
|
||||
<option for(character in state.characters) value=character.id selected=(state.values.characterId==character.id)>${character.name}</option>
|
||||
</select>
|
||||
|
||||
<label>Display Name</label>
|
||||
<input key="characterName" autofocus placeholder=state.placeholder on-change("change", "characterName") value=state.values.characterName />
|
||||
|
||||
<label>IC Date</label>
|
||||
<input key="icdate" placeholder="(Optional)" on-change("change", "fictionalDate") value=state.values.fictionalDate />
|
||||
|
||||
<label>Content</label>
|
||||
<textarea key="source" placeholder="" class="tall" on-change("change", "source") value=state.values.source />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -11,6 +11,8 @@ module.exports = class {
|
||||
title: "",
|
||||
source: "",
|
||||
fictionalDate: "",
|
||||
commentMode: "Article",
|
||||
commentsLocked: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,18 +21,20 @@ module.exports = class {
|
||||
|
||||
onInput(input) {
|
||||
if (input.chapter && !this.first) {
|
||||
let {fictionalDate, title, source} = input.chapter
|
||||
let {fictionalDate, title, source, commentMode, commentsLocked} = input.chapter
|
||||
|
||||
if (fictionalDate != null) {
|
||||
fictionalDate = moment.utc(fictionalDate).format("MMM D, YYYY")
|
||||
} else {
|
||||
fictionalDate = ""
|
||||
}
|
||||
|
||||
this.state.values = {fictionalDate, title, source}
|
||||
this.state.values = {fictionalDate, title, source, commentMode, commentsLocked}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values[key] = ev.target.value
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
@@ -47,15 +51,19 @@ module.exports = class {
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date`
|
||||
if (Number.isNaN(fictionalDate.getTime())) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date.`
|
||||
if (values.fictionalDate.includes("th")) {
|
||||
this.state.error += " Try to remove the 'th'."
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fictionalDate = null
|
||||
}
|
||||
|
||||
const input = {id: this.input.chapter.id, title: values.title, source: values.source}
|
||||
const input = {id: this.input.chapter.id, title: values.title, source: values.source, commentsLocked: values.commentsLocked, commentMode: values.commentMode}
|
||||
|
||||
if (fictionalDate != null) {
|
||||
input.fictionalDate = fictionalDate
|
||||
|
||||
@@ -12,5 +12,17 @@
|
||||
<label>Content</label>
|
||||
<textarea key="source" placeholder="Content" class="tall" on-change("change", "source") value=state.values.source />
|
||||
|
||||
<label>Comments</label>
|
||||
<select key="kind" class="big" placeholder="Kind" on-change("change", "commentMode") value=state.values.commentMode>
|
||||
<option value="Disabled" selected=(state.values.commentMode === "Disabled")>Disabled – Hide all comments</option>
|
||||
<option value="Article" selected=(state.values.commentMode === "Article")>Article – Looks like story chapters</option>
|
||||
<option value="Chat" selected=(state.values.commentMode === "Chat")>Chat – One-line chat messages</option>
|
||||
<option value="Message" selected=(state.values.commentMode === "Message")>Message – Extranet Mail</option>
|
||||
</select>
|
||||
<toggle value=state.values.commentsLocked on="Locked" off="Unlocked"
|
||||
onDesc="Logged-in users can post comments."
|
||||
offDesc="Nobody can post comments to this chapter."
|
||||
on-change("change", "commentsLocked") />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,139 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {commentApi} = require("../../../../../rpdata/api/Comment")
|
||||
const {charactersApi} = require("../../../../../rpdata/api/Character")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
subject: "",
|
||||
source: "",
|
||||
characterName: "",
|
||||
characterId: "",
|
||||
fictionalDate: "",
|
||||
},
|
||||
characters: [],
|
||||
opened: false,
|
||||
placeholder: "(Required)",
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (input.comment) {
|
||||
charactersApi.list({author: (input.user||{}).name}).then((characters) => {
|
||||
this.state.characters = characters.sort((a,b) => a.name.localeCompare(b.name))
|
||||
})
|
||||
|
||||
const comment = input.comment
|
||||
|
||||
let fictionalDate = new Date(comment.fictionalDate)
|
||||
if (comment.fictionalDate && !Number.isNaN(fictionalDate.getTime()) && fictionalDate.getTime() !== 0) {
|
||||
fictionalDate = moment(fictionalDate).format("MMM D, YYYY")
|
||||
} else {
|
||||
fictionalDate = ""
|
||||
}
|
||||
|
||||
this.state.values = {
|
||||
subject: comment.subject,
|
||||
characterId: comment.characterId,
|
||||
characterName: comment.characterName,
|
||||
fictionalDate: fictionalDate,
|
||||
source: comment.source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
|
||||
if (key === "characterId") {
|
||||
if (ev.target.value !== "") {
|
||||
const character = this.state.characters.find(c => c.id === this.state.values.characterId)
|
||||
if (character != null) {
|
||||
this.state.placeholder = character.name
|
||||
} else {
|
||||
this.state.placeholder = "(Required)"
|
||||
}
|
||||
} else {
|
||||
this.state.placeholder = "(Required)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
const values = Object.assign({}, this.state.values)
|
||||
let clearFictionalDate = false
|
||||
|
||||
if (values.source.length == 0) {
|
||||
this.state.error = "The comment body is empty."
|
||||
console.warn(values)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.input.chapter.commentMode !== "Chat" && values.subject.trim().length == 0) {
|
||||
this.state.error = "The subject is empty."
|
||||
return
|
||||
}
|
||||
|
||||
if (values.characterName.trim().length < 1) {
|
||||
const character = this.state.characters.find(c => c.id === this.state.values.characterId)
|
||||
if (character == null) {
|
||||
this.state.error = "You need to give a display name for an anonymous character."
|
||||
return
|
||||
}
|
||||
|
||||
values.characterName = character.name
|
||||
}
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date!`
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fictionalDate = null
|
||||
clearFictionalDate = true
|
||||
}
|
||||
|
||||
const input = {
|
||||
commentId: this.input.comment.id,
|
||||
subject: values.subject,
|
||||
source: values.source,
|
||||
characterName: values.characterName,
|
||||
characterId: values.characterId,
|
||||
fictionalDate: fictionalDate,
|
||||
clearFictionalDate: clearFictionalDate,
|
||||
}
|
||||
|
||||
if (input.characterId == "") {
|
||||
input.characterId = null
|
||||
}
|
||||
|
||||
commentApi.editComment(input).then(comment => {
|
||||
this.emit("edit", comment)
|
||||
this.close()
|
||||
|
||||
this.state.values = {
|
||||
subject: "",
|
||||
source: "",
|
||||
characterName: "",
|
||||
characterId: "",
|
||||
fictionalDate: "",
|
||||
}
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add comemnt:", errs)
|
||||
|
||||
this.state.error = "Failed to add comemnt: " + (errs[0]||errs||{}).message || errs
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") >
|
||||
<h1>Edit Comment</h1>
|
||||
|
||||
<p key="error" class="color-error">${state.error}</p>
|
||||
|
||||
<if(input.chapter.commentMode !== "Chat")>
|
||||
<label>Subject</label>
|
||||
<input key="subject" autofocus placeholder="" class="big" on-change("change", "subject") value=state.values.subject />
|
||||
</if>
|
||||
|
||||
<label>Character ${state.characterId}</label>
|
||||
<select on-change("change", "characterId")>
|
||||
<option value="" selected=state.values.characterId=="">Anonymous</option>
|
||||
<option for(character in state.characters) value=character.id selected=(state.values.characterId==character.id)>${character.name}</option>
|
||||
</select>
|
||||
|
||||
<label>Display Name</label>
|
||||
<input key="characterName" autofocus placeholder=state.placeholder on-change("change", "characterName") value=state.values.characterName />
|
||||
|
||||
<label>IC Date</label>
|
||||
<input key="icdate" placeholder="(Optional)" on-change("change", "fictionalDate") value=state.values.fictionalDate />
|
||||
|
||||
<label>Content</label>
|
||||
<textarea key="source" placeholder="" class="tall" on-change("change", "source") value=state.values.source />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -46,8 +46,12 @@ module.exports = class {
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date`
|
||||
if (Number.isNaN(fictionalDate.getTime())) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date.`
|
||||
if (values.fictionalDate.includes("th")) {
|
||||
this.state.error += " Try to remove the 'th'."
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -36,13 +36,7 @@ module.exports = class {
|
||||
return
|
||||
}
|
||||
|
||||
for (const key in data) {
|
||||
if (!data.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
this.state.story.chapters[index][key] = data[key]
|
||||
}
|
||||
this.state.story.chapters[index] = Object.assign({}, this.state.story.chapters[index], data);
|
||||
|
||||
this.refreshStory()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
const {commentApi} = require("../../../../../rpdata/api/Comment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
title: "",
|
||||
source: "",
|
||||
fictionalDate: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values[key] = ev.target.value
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
doIt() {
|
||||
commentApi.removeComment({commentId: this.input.comment.id}).then(() => {
|
||||
this.emit("remove")
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to delete:", errs)
|
||||
|
||||
this.state.error = "Failed to delete: " + errs.message || errs[0].message
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) notification closable on-close("close") >
|
||||
<h1>Delete Comment</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<p class="color-danger">This is irreversible!</p>
|
||||
|
||||
<button disabled=state.loading on-click("doIt")>${Math.random() > 0.99 ? "Deleet" : "Delete"}</button>
|
||||
</modal>
|
||||
@@ -42,8 +42,12 @@ module.exports = class {
|
||||
|
||||
let fictionalDate = new Date(values.fictionalDate + " UTC")
|
||||
if (values.fictionalDate != "") {
|
||||
if (Number.isNaN(fictionalDate)) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date`
|
||||
if (Number.isNaN(fictionalDate.getTime())) {
|
||||
this.state.error = `Could not parse ${values.fictionalDate} as date.`
|
||||
if (values.fictionalDate.includes("th")) {
|
||||
this.state.error += " Try to remove the 'th'."
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
+11
-9
@@ -28,15 +28,17 @@ module.exports = (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (req.user) {
|
||||
res.locals.user = {
|
||||
loggedIn: true,
|
||||
name: req.user._json.name,
|
||||
permissions: getPermissions(req.user._json.name),
|
||||
}
|
||||
} else {
|
||||
res.locals.user = {
|
||||
loggedIn: false,
|
||||
if (["/static/", "/hax/", "/assets/"].find(p => req.path.startsWith(p)) == null) {
|
||||
if (req.user) {
|
||||
res.locals.user = {
|
||||
loggedIn: true,
|
||||
name: req.user._json.name,
|
||||
permissions: getPermissions(req.user._json.name),
|
||||
}
|
||||
} else {
|
||||
res.locals.user = {
|
||||
loggedIn: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+79
-55
@@ -2308,13 +2308,13 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
|
||||
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz",
|
||||
"integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.9.2",
|
||||
"node-pre-gyp": "^0.10.0"
|
||||
"nan": "^2.12.1",
|
||||
"node-pre-gyp": "^0.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
@@ -2324,7 +2324,8 @@
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -2332,7 +2333,7 @@
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -2342,32 +2343,37 @@
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -2375,15 +2381,15 @@
|
||||
"optional": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
@@ -2426,7 +2432,7 @@
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"version": "7.1.3",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -2444,11 +2450,11 @@
|
||||
"optional": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.21",
|
||||
"version": "0.4.24",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safer-buffer": "^2.1.0"
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ignore-walk": {
|
||||
@@ -2470,7 +2476,8 @@
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -2480,6 +2487,7 @@
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -2492,24 +2500,27 @@
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -2519,37 +2530,38 @@
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"debug": "^2.1.2",
|
||||
"debug": "^4.1.0",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"sax": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.10.0",
|
||||
"version": "0.12.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"needle": "^2.2.0",
|
||||
"needle": "^2.2.1",
|
||||
"nopt": "^4.0.1",
|
||||
"npm-packlist": "^1.1.6",
|
||||
"npmlog": "^4.0.2",
|
||||
"rc": "^1.1.7",
|
||||
"rc": "^1.2.7",
|
||||
"rimraf": "^2.6.1",
|
||||
"semver": "^5.3.0",
|
||||
"tar": "^4"
|
||||
@@ -2565,12 +2577,12 @@
|
||||
}
|
||||
},
|
||||
"npm-bundled": {
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.1.10",
|
||||
"version": "1.4.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -2591,7 +2603,8 @@
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -2601,6 +2614,7 @@
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2635,11 +2649,11 @@
|
||||
"optional": true
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.7",
|
||||
"version": "1.2.8",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"deep-extend": "^0.5.1",
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
@@ -2667,16 +2681,17 @@
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.2",
|
||||
"version": "2.6.3",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"glob": "^7.0.5"
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -2689,7 +2704,7 @@
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"version": "5.7.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
@@ -2706,6 +2721,7 @@
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -2723,6 +2739,7 @@
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -2733,16 +2750,16 @@
|
||||
"optional": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.1",
|
||||
"version": "4.4.8",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"chownr": "^1.1.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.2.4",
|
||||
"minizlib": "^1.1.0",
|
||||
"minipass": "^2.3.4",
|
||||
"minizlib": "^1.1.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
@@ -2752,20 +2769,22 @@
|
||||
"optional": true
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.2"
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3415,6 +3434,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
|
||||
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
|
||||
},
|
||||
"irc-caret-notation": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/irc-caret-notation/-/irc-caret-notation-1.0.0.tgz",
|
||||
"integrity": "sha512-V/Dhqb/wentrfxH5U2pUTK1ye53mIEmpWCpT81Qa+/YfEDY7XuFjpCD/J0WRzjDqaecwn4mj0Mz+6T6bAz/BSA=="
|
||||
},
|
||||
"is-absolute": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
|
||||
@@ -3977,9 +4001,9 @@
|
||||
"integrity": "sha1-OWCLQ1wJAfpVECF8FFJyjWvBm18="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||
},
|
||||
"lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
@@ -4289,9 +4313,9 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||
"version": "2.13.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
|
||||
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
|
||||
"optional": true
|
||||
},
|
||||
"nanomatch": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"graphql-query-compress": "^1.1.0",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"ip": "^1.1.5",
|
||||
"irc-caret-notation": "^1.0.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"lasso": "^3.2.6",
|
||||
|
||||
@@ -68,6 +68,11 @@ class ChangesAPI {
|
||||
time
|
||||
nick
|
||||
}
|
||||
|
||||
...on Comment {
|
||||
chapterId
|
||||
characterName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+51
-4
@@ -1,4 +1,5 @@
|
||||
const {query} = require("../client")
|
||||
const {Comment} = require("./Comment")
|
||||
|
||||
class Chapter {
|
||||
/**
|
||||
@@ -8,9 +9,13 @@ class Chapter {
|
||||
* @param {string} source
|
||||
* @param {string | Date} createdDate
|
||||
* @param {string | Date} fictionalDate
|
||||
* @param {string | Date} editedDate
|
||||
* @param {string | Date} editedDate
|
||||
* @param {boolean} canComment
|
||||
* @param {"Disabled"|"Article"|"Chat"|"Message"} commentMode
|
||||
* @param {boolean} commentsLocked
|
||||
* @param {any[]} comments
|
||||
*/
|
||||
constructor(id, title, author, source, createdDate, fictionalDate, editedDate) {
|
||||
constructor(id, title, author, source, createdDate, fictionalDate, editedDate, canComment, commentMode, commentsLocked, comments) {
|
||||
this.id = id
|
||||
this.title = title
|
||||
this.author = author
|
||||
@@ -18,6 +23,10 @@ class Chapter {
|
||||
this.createdDate = new Date(createdDate)
|
||||
this.fictionalDate = fictionalDate != null ? new Date(fictionalDate) : null
|
||||
this.editedDate = new Date(editedDate)
|
||||
this.canComment = canComment
|
||||
this.commentMode = commentMode
|
||||
this.commentsLocked = commentsLocked
|
||||
this.comments = (comments||[]).map(c => Comment.fromData(c))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +48,31 @@ class ChapterAPI {
|
||||
createdDate
|
||||
fictionalDate
|
||||
editedDate
|
||||
canComment
|
||||
commentMode
|
||||
commentsLocked
|
||||
comments {
|
||||
id
|
||||
subject
|
||||
author
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions: ["member", "story.add"]}).then((data) => {
|
||||
const ac = data.addChapter
|
||||
|
||||
return new Chapter(ac.id, ac.title, ac.author, ac.source, ac.createdDate, ac.fictionalDate, ac.editedDate)
|
||||
return new Chapter(ac.id, ac.title, ac.author, ac.source, ac.createdDate, ac.fictionalDate, ac.editedDate, ac.canComment, ac.commentMode, ac.commentsLocked, ac.comments)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,6 +89,25 @@ class ChapterAPI {
|
||||
title
|
||||
fictionalDate
|
||||
source
|
||||
canComment
|
||||
commentMode
|
||||
commentsLocked
|
||||
comments {
|
||||
id
|
||||
subject
|
||||
author
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions: ["member", "story.edit"]}).then(({editChapter}) => {
|
||||
@@ -68,7 +115,7 @@ class ChapterAPI {
|
||||
editChapter.fictionalDate = new Date(editChapter.fictionalDate)
|
||||
}
|
||||
|
||||
return editChapter
|
||||
return Object.assign({comments:[]}, editChapter)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
const {query} = require("../client")
|
||||
|
||||
class Comment {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} subject
|
||||
* @param {string} author
|
||||
* @param {string} characterName
|
||||
* @param {CommentCharacter} character
|
||||
* @param {string | number | Date} fictionalDate
|
||||
* @param {string | number | Date} createdDate
|
||||
* @param {string | number | Date} editedDate
|
||||
* @param {string} source
|
||||
*/
|
||||
constructor(id, subject, author, characterName, character, fictionalDate, createdDate, editedDate, source) {
|
||||
this.id = id
|
||||
this.subject = subject
|
||||
this.author = author
|
||||
this.characterName = characterName
|
||||
this.character = character != null ? CommentCharacter.fromData(character) : null
|
||||
this.fictionalDate = fictionalDate != null ? new Date(fictionalDate) : null
|
||||
this.createdDate = new Date(createdDate)
|
||||
this.editedDate = new Date(editedDate)
|
||||
this.source = source
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new Comment(data.id, data.subject, data.author, data.characterName, data.character, data.fictionalDate, data.createdDate, data.editedDate, data.source)
|
||||
}
|
||||
}
|
||||
|
||||
class CommentCharacter {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} name
|
||||
* @param {string} author
|
||||
* @param {string} description
|
||||
*/
|
||||
constructor(id, name, author, description) {
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.author = author
|
||||
this.description = description
|
||||
}
|
||||
|
||||
static fromData(data) {
|
||||
return new CommentCharacter(data.id, data.name, data.author, data.description)
|
||||
}
|
||||
}
|
||||
|
||||
class CommentAPI {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {Promise<Comment>}
|
||||
*/
|
||||
find(id) {
|
||||
return query(`
|
||||
query FindComment($id: String!) {
|
||||
comment(id: $id) {
|
||||
id
|
||||
subject
|
||||
author
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
`, {id}).then(({comment}) => {
|
||||
return new Comment(
|
||||
comment.id, comment.subject, comment.author,
|
||||
comment.characterName, comment.character,
|
||||
comment.fictionalDate, comment.createdDate,
|
||||
comment.editedDate, comment.source,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} input
|
||||
* @returns {Promise<Comment>}
|
||||
*/
|
||||
addComment(input) {
|
||||
return query(`
|
||||
mutation AddComment($input: CommentAddInput!) {
|
||||
addComment(input: $input) {
|
||||
id
|
||||
subject
|
||||
author
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions:["member", "story.edit"]}).then(({addComment: comment}) => {
|
||||
return new Comment(
|
||||
comment.id, comment.subject, comment.author,
|
||||
comment.characterName, comment.character,
|
||||
comment.fictionalDate, comment.createdDate,
|
||||
comment.editedDate, comment.source,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} input
|
||||
* @returns {Promise<Comment>}
|
||||
*/
|
||||
editComment(input) {
|
||||
return query(`
|
||||
mutation EditComment($input: CommentEditInput!) {
|
||||
editComment(input: $input) {
|
||||
id
|
||||
subject
|
||||
author
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions:["member", "story.edit"]}).then(({editComment: comment}) => {
|
||||
return new Comment(
|
||||
comment.id, comment.subject, comment.author,
|
||||
comment.characterName, comment.character,
|
||||
comment.fictionalDate, comment.createdDate,
|
||||
comment.editedDate, comment.source,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{commentId:string}} input
|
||||
* @returns {Promise<{commentId:string}>}
|
||||
*/
|
||||
removeComment(input) {
|
||||
return query(`
|
||||
mutation RemoveComment($input: CommentRemoveInput!) {
|
||||
removeComment(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions:["member", "story.edit"]})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Comment, CommentCharacter, commentApi: new CommentAPI}
|
||||
+35
-16
@@ -29,7 +29,7 @@ class Story {
|
||||
this.updatedDate = new Date(updatedDate)
|
||||
this.fictionalDate = fictionalDate != null ? new Date(fictionalDate) : null
|
||||
this.tags = tags.map(dt => new Tag(dt.kind, dt.name))
|
||||
this.chapters = chapters != null ? chapters.map(c => new Chapter(c.id, c.title, c.author, c.source, c.createdDate, c.fictionalDate, c.editedDate)) : null
|
||||
this.chapters = chapters != null ? chapters.map(c => new Chapter(c.id, c.title, c.author, c.source, c.createdDate, c.fictionalDate, c.editedDate, c.canComment, c.commentMode, c.commentsLocked, c.comments)) : null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,32 +91,51 @@ const storyApi = {
|
||||
*/
|
||||
find(id) {
|
||||
return query(`
|
||||
query FindStory($id: String!) {
|
||||
story(id: $id) {
|
||||
id
|
||||
query FindStory($id: String!) {
|
||||
story(id: $id) {
|
||||
id
|
||||
name
|
||||
author
|
||||
category
|
||||
tags {
|
||||
kind
|
||||
name
|
||||
}
|
||||
listed
|
||||
open
|
||||
createdDate
|
||||
fictionalDate
|
||||
updatedDate
|
||||
chapters {
|
||||
id
|
||||
title
|
||||
author
|
||||
category
|
||||
tags {
|
||||
kind
|
||||
name
|
||||
}
|
||||
listed
|
||||
open
|
||||
source
|
||||
createdDate
|
||||
fictionalDate
|
||||
updatedDate
|
||||
chapters {
|
||||
editedDate
|
||||
canComment
|
||||
commentMode
|
||||
commentsLocked
|
||||
comments {
|
||||
id
|
||||
title
|
||||
subject
|
||||
author
|
||||
source
|
||||
createdDate
|
||||
characterName
|
||||
character {
|
||||
id
|
||||
name
|
||||
author
|
||||
description
|
||||
}
|
||||
fictionalDate
|
||||
createdDate
|
||||
editedDate
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {id}).then(({story}) => {
|
||||
return new Story(
|
||||
story.id, story.name, story.author, story.category,
|
||||
|
||||
Reference in New Issue
Block a user