story: Added add story modal, reorganized story page to allow modals and live updates.
This commit is contained in:
@@ -14,6 +14,15 @@ div.overlay {
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
opacity: 0.5;
|
||||
margin: 0;
|
||||
margin-top: 1em;
|
||||
border-bottom: 0.5px solid;
|
||||
padding-bottom: 0.125em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.modal {
|
||||
width: 60ch;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const {tagApi} = require("../../../../../rpdata/api/Tag")
|
||||
const {tagApi} = require("../../../rpdata/api/Tag")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
<div class="tag-options">
|
||||
<input key="name" placeholder="Name" class="big" on-change("change", "name") value=state.values.name />
|
||||
<select key="kind" placeholder="Kind" class="big" on-change("change", "kind") value=state.values.kind>
|
||||
<label>Tag Name</label>
|
||||
<input key="name" placeholder="Name" on-change("change", "name") value=state.values.name />
|
||||
<label>Tag Kind</label>
|
||||
<select key="kind" placeholder="Kind" on-change("change", "kind") value=state.values.kind>
|
||||
<option value="Character" selected=(state.values.kind === "Character")>Character</option>
|
||||
<option value="Event" selected=(state.values.kind === "Event")>Event</option>
|
||||
<option value="Location" selected=(state.values.kind === "Location")>Location</option>
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<p key="error" class="color-error">${state.error}</p>
|
||||
|
||||
<h2>Meta</h2>
|
||||
<label>Title</label>
|
||||
<input key="name" placeholder="Name" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
@@ -14,7 +15,7 @@
|
||||
<option for(category in input.categories) value=category.name selected=(state.values.category === category.name)>${category.name}</option>
|
||||
</select>
|
||||
|
||||
<label>Options</label>
|
||||
<h2>Options</h2>
|
||||
<toggle value=state.values.listed on="Listed" off="Unlisted"
|
||||
onDesc="The story will be visible on the front page."
|
||||
offDesc="A direct link is required to view this story."
|
||||
@@ -24,5 +25,6 @@
|
||||
offDesc="Only you can add chapters to this story."
|
||||
on-change("change", "open") />
|
||||
|
||||
<h2></h2>
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,79 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {storyApi} = require("../../../../../rpdata/api/Story")
|
||||
|
||||
module.exports = class {
|
||||
onCreate() {
|
||||
this.state = {
|
||||
error: null,
|
||||
values: {
|
||||
name: "",
|
||||
fictionalDate: "",
|
||||
category: "Info",
|
||||
open: false,
|
||||
listed: false,
|
||||
},
|
||||
tags: [],
|
||||
loading: false,
|
||||
}
|
||||
|
||||
this.filled = false
|
||||
}
|
||||
|
||||
addTag(tag) {
|
||||
if (tag.name == "" || tag.kind == "") {
|
||||
return
|
||||
}
|
||||
|
||||
this.state.tags = this.state.tags.filter(t => t.kind !== tag.kind || t.name !== tag.name).concat([tag])
|
||||
}
|
||||
|
||||
removeTag(tag) {
|
||||
this.state.tags = this.state.tags.filter(t => t.kind !== tag.kind || t.name !== tag.name)
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values[key] = ev.target.value
|
||||
this.state.values = Object.assign({}, this.state.values)
|
||||
}
|
||||
|
||||
save() {
|
||||
const values = this.state.values
|
||||
|
||||
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 = {name: values.name, category: values.category, open: values.open, listed: values.listed, tags: this.state.tags}
|
||||
if (fictionalDate != null) {
|
||||
input.fictionalDate = fictionalDate
|
||||
}
|
||||
|
||||
console.log(input)
|
||||
|
||||
this.state.loading = true
|
||||
storyApi.add(input).then(data => {
|
||||
this.emit("add", data)
|
||||
this.emit("close")
|
||||
|
||||
this.state.tags = []
|
||||
this.state.values = {name: "", fictionalDate: "", category: "", open: "", listed: ""}
|
||||
|
||||
window.location = `/story/${data.id}/`
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to edit:", errs)
|
||||
this.state.error = "Failed to edit: " + errs[0].message
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<modal class="modal color-text nolabel" key="modal2" enabled=(input.enabled) closable on-close("close") >
|
||||
<h1>Edit Story</h1>
|
||||
|
||||
<p key="error" class="color-error">${state.error}</p>
|
||||
|
||||
<h2>Meta</h2>
|
||||
<label>Title</label>
|
||||
<input key="name" placeholder="Name" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
<label>IC Date</label>
|
||||
<input key="icdate" placeholder="IC Date" on-change("change", "fictionalDate") value=state.values.fictionalDate />
|
||||
|
||||
<label>Category</label>
|
||||
<select key="category" placeholder="Kind" on-change("change", "category")>
|
||||
<option for(category in input.categories) value=category.name selected=(state.values.category === category.name)>${category.name}</option>
|
||||
</select>
|
||||
|
||||
<h2>Tags</h2>
|
||||
<story-tag-options small tags=state.tags loading=state.loading on-remove("removeTag") on-add("addTag") />
|
||||
|
||||
<h2>Options</h2>
|
||||
<toggle value=state.values.listed on="Listed" off="Unlisted"
|
||||
onDesc="The story will be visible on the front page."
|
||||
offDesc="A direct link is required to view this story."
|
||||
on-change("change", "listed") />
|
||||
<toggle value=state.values.open on="Open" off="Closed"
|
||||
onDesc="Other authors can add chapters to this story."
|
||||
offDesc="Only you can add chapters to this story."
|
||||
on-change("change", "open") />
|
||||
|
||||
<h2></h2>
|
||||
<button disabled=state.loading on-click("save")>Add Story</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,16 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
stories: input.stories,
|
||||
modal: null,
|
||||
}
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<story-menu categories=input.categories selected=(input.selected || {}) menuTags=input.menuTags user=input.user on-open("open") />
|
||||
<main>
|
||||
<story-list stories=state.stories />
|
||||
<add-story-modal enabled=(state.modal === "story.add") stories=input.stories categories=input.categories on-close("close") />
|
||||
</main>
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = class {
|
||||
open(modalName) {
|
||||
this.emit("open", modalName)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<menu user=input.user>
|
||||
<menu-header>Story</menu-header>
|
||||
<menu-link key="index" selected=input.selected.index icon="S" href="/story/">Stories</menu-link>
|
||||
<menu-link dark key="_create" on-click("open", "story.add") icon="+">Add Story</menu-link>
|
||||
<menu-gap />
|
||||
<menu-header>Categories</menu-header>
|
||||
<menu-link for(category in (input.categories||[])) key=(category.name) selected=(input.selected.category == category.name) href=`/story/by-category/${category.name}` icon=category.name.charAt(0)>${category.name}</menu-link>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<include("../layout", {title: "Stories", site: "story"})>
|
||||
<@body>
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<story-menu categories=input.categories selected=(input.selected || {}) menuTags=input.menuTags user=input.user />
|
||||
<main>
|
||||
<story-list stories=input.stories />
|
||||
</main>
|
||||
<page user=input.user stories=input.stories categories=input.categories />
|
||||
</@body>
|
||||
</include>
|
||||
|
||||
@@ -194,6 +194,50 @@ const storyApi = {
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Call `addStory(input)` mutation, returns the ID.
|
||||
*
|
||||
* @param {{name:string, category:string, author:string, open:boolean, listed:boolean, fictionalDate:Date, tags:Tag[]}} input
|
||||
* @returns {Promise<Story>}
|
||||
*/
|
||||
add(input) {
|
||||
return query(`
|
||||
mutation AddStory($input: StoryAddInput!) {
|
||||
addStory(input:$input) {
|
||||
id
|
||||
name
|
||||
author
|
||||
category
|
||||
tags {
|
||||
kind
|
||||
name
|
||||
}
|
||||
listed
|
||||
open
|
||||
createdDate
|
||||
fictionalDate
|
||||
updatedDate
|
||||
chapters {
|
||||
id
|
||||
title
|
||||
author
|
||||
source
|
||||
createdDate
|
||||
fictionalDate
|
||||
editedDate
|
||||
}
|
||||
}
|
||||
}
|
||||
`, {input}, {permissions: ["member", "story.edit"]}).then(({addStory}) => {
|
||||
return new Story(
|
||||
addStory.id, addStory.name, addStory.author, addStory.category,
|
||||
addStory.listed, addStory.open,
|
||||
addStory.createdDate, addStory.updatedDate, addStory.fictionalDate,
|
||||
addStory.tags, addStory.chapters
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Call `editStory(input)` mutation, returns the changable fields.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user