UI: Started on story content
This commit is contained in:
Executable
BIN
Binary file not shown.
@@ -2,6 +2,7 @@ import React, { Component } from 'react'
|
|||||||
import { Route, Redirect, Switch } from "react-router-dom";
|
import { Route, Redirect, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import StoryRoot from './routes/story/StoryRoot'
|
import StoryRoot from './routes/story/StoryRoot'
|
||||||
|
import StoryContentRoot from './routes/story-content/StoryContentRoot'
|
||||||
import LogsRoot from './routes/logs/LogsRoot'
|
import LogsRoot from './routes/logs/LogsRoot'
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
@@ -10,6 +11,7 @@ export default class App extends Component {
|
|||||||
<div className="App">
|
<div className="App">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Redirect exact path="/" to="/story/" />
|
<Redirect exact path="/" to="/story/" />
|
||||||
|
<Route path="/story/:id(S[0-9a-z]{15})" component={StoryContentRoot}/>
|
||||||
<Route path="/story/" component={StoryRoot}/>
|
<Route path="/story/" component={StoryRoot}/>
|
||||||
<Route path="/logs/" component={LogsRoot}/>
|
<Route path="/logs/" component={LogsRoot}/>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ export default class Menu extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MenuLink({ icon, children, tagType, to, onClick }) {
|
export function MenuLink({ icon, children, tagKind, to, onClick }) {
|
||||||
const isSelected = window.location.pathname === to
|
const isSelected = window.location.pathname === to
|
||||||
const className = isSelected ? "selected" : "not-selected"
|
const className = isSelected ? "selected" : "not-selected"
|
||||||
const textClass = isSelected ? "color-primary" : `color-tag-${tagType||"none"}`
|
const textClass = isSelected ? "color-primary" : `color-tag-${(tagKind||"none").toLowerCase()}`
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<div className={`MenuLink color-menu ${className}`} onClick={onClick}>
|
<div className={`MenuLink color-menu ${className}`} onClick={onClick}>
|
||||||
|
|||||||
@@ -47,10 +47,16 @@
|
|||||||
width: 3ch;
|
width: 3ch;
|
||||||
padding: 0.15em 0.125em;
|
padding: 0.15em 0.125em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MenuLink .text {
|
.MenuLink .text {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
max-width: 22ch;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MenuGap {
|
.MenuGap {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import React, { Component } from "react"
|
||||||
|
import moment from "moment"
|
||||||
|
|
||||||
|
import Menu, { MenuHeader, MenuLink, MenuGap } from '../../common/Menu'
|
||||||
|
|
||||||
|
export default class StoryContentMenu extends Component {
|
||||||
|
render() {
|
||||||
|
const { story } = this.props
|
||||||
|
|
||||||
|
console.log(story.tags)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<MenuHeader>Chapters</MenuHeader>
|
||||||
|
<ChapterMenu storyId={story.id} chapters={story.chapters} />
|
||||||
|
<MenuGap />
|
||||||
|
<MenuHeader>Tags</MenuHeader>
|
||||||
|
<TagMenu tags={story.tags} />
|
||||||
|
<MenuGap />
|
||||||
|
<MenuHeader>Story</MenuHeader>
|
||||||
|
<MonthMenuItem date={story.fictionalDate} />
|
||||||
|
<MenuLink to={`/story/author/${story.author}`} icon="A">{story.author}</MenuLink>
|
||||||
|
<MenuGap />
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChapterMenu({chapters, storyId}) {
|
||||||
|
return chapters.map((chapter, index) => (
|
||||||
|
<MenuLink to={`/story/${storyId}/${chapter.id}`} icon={index + 1}>{chapter.title}</MenuLink>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
function TagMenu({tags}) {
|
||||||
|
return tags.map(tag => (
|
||||||
|
<MenuLink tagKind={tag.kind} to={`/story/tag/${tag.kind}/${tag.name}`} icon={tag.kind.charAt(0)}>{tag.name}</MenuLink>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
function MonthMenuItem({date}) {
|
||||||
|
const fictionalDate = moment.utc(date)
|
||||||
|
if (fictionalDate.year() < 100) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const monthKey = fictionalDate.format("YYYY-MM")
|
||||||
|
const monthText = fictionalDate.format("MMM D, YYYY")
|
||||||
|
|
||||||
|
return <MenuLink to={`/story/month/${monthKey}`} icon="D">{monthText}</MenuLink>
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { Component } from "react"
|
||||||
|
import { Route, Switch, Redirect } from "react-router-dom";
|
||||||
|
import { Query } from "react-apollo"
|
||||||
|
|
||||||
|
import Background from "../../common/Background"
|
||||||
|
import { LoadingScreen } from "../../common/LoadingScreen"
|
||||||
|
import StoryContentMenu from "./StoryContentMenu"
|
||||||
|
|
||||||
|
import storyContentRoot from "./gql/StoryContentRoot";
|
||||||
|
|
||||||
|
class StoryContentRoot extends Component {
|
||||||
|
render() {
|
||||||
|
const story = this.props.story
|
||||||
|
|
||||||
|
const chapterRedirect = story.chapters.length > 0 ? (
|
||||||
|
<Redirect exact from={`/story/${story.id}/`} to={`/story/${story.id}/${story.chapters[0].id}`} />
|
||||||
|
) : null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="StoryContentRoot theme-story">
|
||||||
|
<Background />
|
||||||
|
<StoryContentMenu story={this.props.story} />
|
||||||
|
<Switch>
|
||||||
|
{chapterRedirect}
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({id, match}) => (
|
||||||
|
<Query query={storyContentRoot} variables={{id: id || match.params.id}}>
|
||||||
|
{({ loading, error, data }) => {
|
||||||
|
if (loading) {
|
||||||
|
return <LoadingScreen className="theme-story" />
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return `Error! ${error.message}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return <StoryContentRoot {...data} />
|
||||||
|
}}
|
||||||
|
</Query>
|
||||||
|
)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import gql from "graphql-tag"
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
query StoryContentRoot($id: String!) {
|
||||||
|
story(id: $id) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
author
|
||||||
|
category
|
||||||
|
createdDate
|
||||||
|
fictionalDate
|
||||||
|
updatedDate
|
||||||
|
tags {
|
||||||
|
kind
|
||||||
|
name
|
||||||
|
}
|
||||||
|
chapters {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
author
|
||||||
|
source
|
||||||
|
createdDate
|
||||||
|
fictionalDate
|
||||||
|
editedDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@@ -23,6 +23,7 @@ export class StoryIndex extends Component {
|
|||||||
<Route exact path="/story/" component={() => <StoryList filter={{}} />} />
|
<Route exact path="/story/" component={() => <StoryList filter={{}} />} />
|
||||||
<Route exact path="/story/open/" component={() => <StoryList filter={{open: true}} />} />
|
<Route exact path="/story/open/" component={() => <StoryList filter={{open: true}} />} />
|
||||||
<Route exact path="/story/tags/" component={StoryTags} />
|
<Route exact path="/story/tags/" component={StoryTags} />
|
||||||
|
<Route exact path="/story/tag/:kind/:name/" component={({match}) => <StoryList filter={{tags: [match.params]}} />} />
|
||||||
<Route exact path="/story/category/:category/" component={({match: {params: {category}}}) => <StoryList filter={{category}} />} />
|
<Route exact path="/story/category/:category/" component={({match: {params: {category}}}) => <StoryList filter={{category}} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user