Gisle Aune
6 years ago
20 changed files with 642 additions and 17 deletions
-
5marko/components/date/index.marko
-
1marko/components/markdown/index.marko
-
68marko/global/base.less
-
6marko/global/colors.less
-
10marko/page/story-content/components/chapter-meta/index.marko
-
4marko/page/story-content/components/chapter-meta/style.less
-
5marko/page/story-content/components/chapter/component.js
-
14marko/page/story-content/components/chapter/index.marko
-
26marko/page/story-content/components/chapter/style.less
-
18marko/page/story-content/components/comment/component.js
-
110marko/page/story-content/components/comment/index.marko
-
113marko/page/story-content/components/comment/style.less
-
12marko/page/story-content/components/create-chapter-modal/component.js
-
8marko/page/story-content/components/create-chapter-modal/index.marko
-
96marko/page/story-content/components/create-comment-modal/component.js
-
27marko/page/story-content/components/create-comment-modal/index.marko
-
10marko/page/story-content/components/edit-chapter-modal/component.js
-
12marko/page/story-content/components/edit-chapter-modal/index.marko
-
42rpdata/api/Chapter.js
-
72rpdata/api/Comment.js
@ -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> |
@ -1,3 +1,4 @@ |
|||||
<div class=["markdown-content", input.class]> |
<div class=["markdown-content", input.class]> |
||||
|
<include(input.renderBody) /> |
||||
<include(state.render) /> |
<include(state.render) /> |
||||
</div> |
</div> |
@ -1,6 +1,14 @@ |
|||||
<if(state.text != null && state.text != "")> |
<if(state.text != null && state.text != "")> |
||||
<div class=["chapter-meta", input.kind, state.color, input.weak ? "weak" : null] title=state.tooltip> |
<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> |
<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> |
||||
|
</span> |
||||
</div> |
</div> |
||||
</if> |
</if> |
@ -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,110 @@ |
|||||
|
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("open", "editComment", input.comment) >Edit</a> |
||||
|
</if-permitted> |
||||
|
<if-permitted user=input.user author=input.comment.author permission="story.edit"> |
||||
|
<a on-click("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>${input.comment.characterName}</span> |
||||
|
</div> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr if(input.comment.fictionalDate != null)> |
||||
|
<th>Date:</th> |
||||
|
<td>${moment(input.comment.fictionalDate).format("MMM D, YYYY")}</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("open", "editComment", input.comment) >Edit</a> |
||||
|
</if-permitted> |
||||
|
<if-permitted user=input.user author=input.comment.author permission="story.edit"> |
||||
|
<a on-click("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> |
||||
|
<span class="decoration color-menu">[</span> |
||||
|
<date class="color-menu" value=input.comment.fictionalDate></date> |
||||
|
<span class="decoration color-menu">] </span> |
||||
|
<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("open", "editComment", input.comment) >Edit</a> |
||||
|
</if-permitted> |
||||
|
<if-permitted user=input.user author=input.comment.author permission="story.edit"> |
||||
|
<a on-click("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 /> |
||||
|
<chapter-meta 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; |
||||
|
} |
||||
|
} |
@ -0,0 +1,96 @@ |
|||||
|
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, |
||||
|
} |
||||
|
|
||||
|
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)) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
change(key, ev) { |
||||
|
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value}) |
||||
|
} |
||||
|
|
||||
|
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 = this.state.values |
||||
|
|
||||
|
if (values.source.length < 1) { |
||||
|
this.state.error = "You cannot post an empty comment." |
||||
|
console.warn(values) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
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, |
||||
|
} |
||||
|
|
||||
|
commentApi.addComment(input).then(chapter => { |
||||
|
this.emit("add", chapter) |
||||
|
this.emit("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") on-open("open") > |
||||
|
<h1>Create Chapter</h1> |
||||
|
|
||||
|
<p key="error" class="color-error">${state.error}</p> |
||||
|
|
||||
|
<if(input.chapter.commentMode !== "Chat")> |
||||
|
<label>Subject</label> |
||||
|
<input key="subject" autofocus placeholder="(Optional)" class="big" on-change("change", "subject") value=state.values.subject /> |
||||
|
</if> |
||||
|
|
||||
|
<label>Diaply Name</label> |
||||
|
<input key="characterName" autofocus placeholder="(Optional)" on-change("change", "characterName") value=state.values.characterName /> |
||||
|
|
||||
|
<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>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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue