UI: Started on story content
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { Component } from 'react'
|
||||
import { Route, Redirect, Switch } from "react-router-dom";
|
||||
|
||||
import StoryRoot from './routes/story/StoryRoot'
|
||||
import StoryContentRoot from './routes/story-content/StoryContentRoot'
|
||||
import LogsRoot from './routes/logs/LogsRoot'
|
||||
|
||||
export default class App extends Component {
|
||||
@@ -10,6 +11,7 @@ export default class App extends Component {
|
||||
<div className="App">
|
||||
<Switch>
|
||||
<Redirect exact path="/" to="/story/" />
|
||||
<Route path="/story/:id(S[0-9a-z]{15})" component={StoryContentRoot}/>
|
||||
<Route path="/story/" component={StoryRoot}/>
|
||||
<Route path="/logs/" component={LogsRoot}/>
|
||||
</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 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 = (
|
||||
<div className={`MenuLink color-menu ${className}`} onClick={onClick}>
|
||||
|
||||
@@ -47,10 +47,16 @@
|
||||
width: 3ch;
|
||||
padding: 0.15em 0.125em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.MenuLink .text {
|
||||
display: inline-block;
|
||||
max-width: 22ch;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.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/open/" component={() => <StoryList filter={{open: true}} />} />
|
||||
<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}} />} />
|
||||
</Switch>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user