Browse Source

Comments are pretty much done!

master
Gisle Aune 6 years ago
parent
commit
6ea9632623
  1. 3
      marko/components/modal/component.js
  2. 7
      marko/page/story-content/components/chapter-meta/component.js
  3. 4
      marko/page/story-content/components/chapter-meta/index.marko
  4. 28
      marko/page/story-content/components/chapter/component.js
  5. 3
      marko/page/story-content/components/chapter/index.marko
  6. 21
      marko/page/story-content/components/comment/index.marko
  7. 51
      marko/page/story-content/components/create-comment-modal/component.js
  8. 10
      marko/page/story-content/components/create-comment-modal/index.marko
  9. 131
      marko/page/story-content/components/edit-comment-modal/component.js
  10. 27
      marko/page/story-content/components/edit-comment-modal/index.marko
  11. 8
      marko/page/story-content/components/page/component.js
  12. 36
      marko/page/story-content/components/remove-comment-modal/component.js
  13. 9
      marko/page/story-content/components/remove-comment-modal/index.marko
  14. 50
      rpdata/api/Comment.js

3
marko/components/modal/component.js

@ -47,6 +47,9 @@ module.exports = class {
if (this.input.enabled) { if (this.input.enabled) {
window.removeEventListener("keydown", this.onPress) window.removeEventListener("keydown", this.onPress)
} }
this.mounted = false
this.opened = false
} }
/** /**

7
marko/page/story-content/components/chapter-meta/component.js

@ -51,6 +51,13 @@ module.exports = class {
break break
} }
case "title": {
this.state.color = "color-primary"
this.state.text = value
break
}
default: { default: {
this.state.color = "color-menu" this.state.color = "color-menu"
this.state.text = value this.state.text = value

4
marko/page/story-content/components/chapter-meta/index.marko

@ -9,6 +9,10 @@
<h2>${input.character.author}</h2> <h2>${input.character.author}</h2>
<markdown source=input.character.description /> <markdown source=input.character.description />
</div> </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> </span>
</div> </div>
</if> </if>

28
marko/page/story-content/components/chapter/component.js

@ -8,9 +8,37 @@ module.exports = class {
Message: "Message", Message: "Message",
Chat: "Chat 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) { updateChapter(data) {
this.emit("edit", data) this.emit("edit", data)
} }

3
marko/page/story-content/components/chapter/index.marko

@ -25,6 +25,7 @@
locked=input.chapter.commentsLocked locked=input.chapter.commentsLocked
mode=input.chapter.commentMode mode=input.chapter.commentMode
user=input.user user=input.user
on-open("onOpenComment", comment)
/> />
<if-permitted if(input.chapter.canComment) user=input.user author=input.chapter.author permission="chapter.edit"> <if-permitted if(input.chapter.canComment) user=input.user author=input.chapter.author permission="chapter.edit">
<div class="add-comment color-menu" on-click("open", "addComment") >Add ${state.commentLabels[input.chapter.commentMode] || "Comment"}</div> <div class="add-comment color-menu" on-click("open", "addComment") >Add ${state.commentLabels[input.chapter.commentMode] || "Comment"}</div>
@ -32,6 +33,8 @@
</div> </div>
<edit-chapter-modal enabled=(state.modal === "edit") chapter=input.chapter on-close("close") on-edit("updateChapter") /> <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") /> <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") /> <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") /> <remove-chapter-modal enabled=(state.modal === "remove") chapter=input.chapter on-close("close") on-remove("removeChapter") />
</div> </div>

21
marko/page/story-content/components/comment/index.marko

@ -5,10 +5,10 @@ import moment from "moment"
<div class="metadata"> <div class="metadata">
<div class="options color-menu"> <div class="options color-menu">
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "editComment", input.comment) >Edit</a>
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
</if-permitted> </if-permitted>
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "removeComment", input.comment) >Remove</a>
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
</if-permitted> </if-permitted>
</div> </div>
<chapter-meta weak kind="date" value=input.comment.createdDate /> <chapter-meta weak kind="date" value=input.comment.createdDate />
@ -34,7 +34,14 @@ import moment from "moment"
</div> </div>
</div> </div>
<div else> <div else>
<span class="tooltip color-primary">
<span>${input.comment.characterName}</span> <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> </div>
</td> </td>
</tr> </tr>
@ -51,10 +58,10 @@ import moment from "moment"
<div class="content"> <div class="content">
<div class="options color-menu"> <div class="options color-menu">
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "editComment", input.comment) >Edit</a>
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
</if-permitted> </if-permitted>
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "removeComment", input.comment) >Remove</a>
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
</if-permitted> </if-permitted>
</div> </div>
<for (paragraph in state.paragraphs)> <for (paragraph in state.paragraphs)>
@ -91,14 +98,14 @@ import moment from "moment"
<div class="metadata"> <div class="metadata">
<div class="options color-menu"> <div class="options color-menu">
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "editComment", input.comment) >Edit</a>
<a on-click("emit", "open", "editComment", input.comment) >Edit</a>
</if-permitted> </if-permitted>
<if-permitted user=input.user author=input.comment.author permission="story.edit"> <if-permitted user=input.user author=input.comment.author permission="story.edit">
<a on-click("open", "removeComment", input.comment) >Remove</a>
<a on-click("emit", "open", "removeComment", input.comment) >Remove</a>
</if-permitted> </if-permitted>
</div> </div>
<chapter-meta kind="title" value=input.comment.subject /> <chapter-meta kind="title" value=input.comment.subject />
<chapter-meta kind="name" value=input.comment.characterName character=input.comment.character />
<chapter-meta kind="name" value=input.comment.characterName character=(input.comment.character) ukAuthor=input.comment.author />
<chapter-meta kind="date" value=input.comment.fictionalDate /> <chapter-meta kind="date" value=input.comment.fictionalDate />
<chapter-meta weak kind="date" value=input.comment.createdDate /> <chapter-meta weak kind="date" value=input.comment.createdDate />
<chapter-meta weak kind="author" value=input.comment.author /> <chapter-meta weak kind="author" value=input.comment.author />

51
marko/page/story-content/components/create-comment-modal/component.js

@ -17,6 +17,7 @@ module.exports = class {
}, },
characters: [], characters: [],
opened: false, opened: false,
placeholder: "(Required)",
} }
this.first = false this.first = false
@ -26,10 +27,30 @@ module.exports = class {
charactersApi.list({author: (this.input.user||{}).name}).then((characters) => { charactersApi.list({author: (this.input.user||{}).name}).then((characters) => {
this.state.characters = characters.sort((a,b) => a.name.localeCompare(b.name)) this.state.characters = characters.sort((a,b) => a.name.localeCompare(b.name))
}) })
const comments = this.input.chapter.comments || []
const lastComment = comments[comments.length - 1]
let date = lastComment.fictionalDate || chapter.fictionalDate
if (date != null) {
this.change("fictionalDate", {target: {value: moment(date).format("MMM D, YYYY")}})
}
} }
change(key, ev) { change(key, ev) {
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value}) 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() { open() {
@ -47,14 +68,29 @@ module.exports = class {
} }
save() { save() {
const values = this.state.values
const values = Object.assign({}, this.state.values)
if (values.source.length < 1) {
this.state.error = "You cannot post an empty comment."
if (values.source.length == 0) {
this.state.error = "The comment body is empty."
console.warn(values) console.warn(values)
return 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") let fictionalDate = new Date(values.fictionalDate + " UTC")
if (values.fictionalDate != "") { if (values.fictionalDate != "") {
if (Number.isNaN(fictionalDate)) { if (Number.isNaN(fictionalDate)) {
@ -74,8 +110,12 @@ module.exports = class {
fictionalDate: fictionalDate, fictionalDate: fictionalDate,
} }
commentApi.addComment(input).then(chapter => {
this.emit("add", chapter)
if (input.characterId == "") {
input.characterId = null
}
commentApi.addComment(input).then(comment => {
this.emit("add", comment)
this.emit("close") this.emit("close")
this.state.values = { this.state.values = {
@ -85,6 +125,7 @@ module.exports = class {
characterId: "", characterId: "",
fictionalDate: "", fictionalDate: "",
} }
this.state.opened = false
}).catch(errs => { }).catch(errs => {
console.warn("Failed to add comemnt:", errs) console.warn("Failed to add comemnt:", errs)

10
marko/page/story-content/components/create-comment-modal/index.marko

@ -1,22 +1,22 @@
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") > <modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
<h1>Create Chapter</h1>
<h1>Add Comment</h1>
<p key="error" class="color-error">${state.error}</p> <p key="error" class="color-error">${state.error}</p>
<if(input.chapter.commentMode !== "Chat")> <if(input.chapter.commentMode !== "Chat")>
<label>Subject</label> <label>Subject</label>
<input key="subject" autofocus placeholder="(Optional)" class="big" on-change("change", "subject") value=state.values.subject />
<input key="subject" autofocus placeholder="" class="big" on-change("change", "subject") value=state.values.subject />
</if> </if>
<label>Diaply Name</label>
<input key="characterName" autofocus placeholder="(Optional)" on-change("change", "characterName") value=state.values.characterName />
<label>Character ${state.characterId}</label> <label>Character ${state.characterId}</label>
<select on-change("change", "characterId")> <select on-change("change", "characterId")>
<option value="" selected=state.values.characterId=="">Anonymous</option> <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> <option for(character in state.characters) value=character.id selected=(state.values.characterId==character.id)>${character.name}</option>
</select> </select>
<label>Display Name</label>
<input key="characterName" autofocus placeholder=state.placeholder on-change("change", "characterName") value=state.values.characterName />
<label>IC Date</label> <label>IC Date</label>
<input key="icdate" placeholder="(Optional)" on-change("change", "fictionalDate") value=state.values.fictionalDate /> <input key="icdate" placeholder="(Optional)" on-change("change", "fictionalDate") value=state.values.fictionalDate />

131
marko/page/story-content/components/edit-comment-modal/component.js

@ -0,0 +1,131 @@
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
this.state.values = {
subject: comment.subject,
characterId: comment.characterId,
characterName: comment.characterName,
fictionalDate: moment(comment.fictionalDate).format("MMM D, YYYY"),
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
})
}
}

27
marko/page/story-content/components/edit-comment-modal/index.marko

@ -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>

8
marko/page/story-content/components/page/component.js

@ -36,13 +36,7 @@ module.exports = class {
return 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() this.refreshStory()
} }

36
marko/page/story-content/components/remove-comment-modal/component.js

@ -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
})
}
}

9
marko/page/story-content/components/remove-comment-modal/index.marko

@ -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>

50
rpdata/api/Comment.js

@ -107,7 +107,7 @@ class CommentAPI {
source source
} }
} }
`, {input}).then(({comment}) => {
`, {input}, {permissions:["member", "story.edit"]}).then(({addComment: comment}) => {
return new Comment( return new Comment(
comment.id, comment.subject, comment.author, comment.id, comment.subject, comment.author,
comment.characterName, comment.character, comment.characterName, comment.character,
@ -116,6 +116,54 @@ class CommentAPI {
) )
}) })
} }
/**
* @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} module.exports = {Comment, CommentCharacter, commentApi: new CommentAPI}
Loading…
Cancel
Save