package: Removed rpdata-ui.
This commit is contained in:
@@ -1,21 +0,0 @@
|
|||||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# User Interface
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
This is a UI created with `create-react-app`, so the process of getting started on a new host is navigating to the directory and running these commands.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
## Release
|
|
||||||
|
|
||||||
This is built and packaged with the parent project.
|
|
||||||
Generated
-12110
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "rpdata-ui",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"ajv": "^6.5.2",
|
|
||||||
"apollo-boost": "^0.1.10",
|
|
||||||
"apollo-cache-inmemory": "^1.3.0-beta.6",
|
|
||||||
"graphql": "^0.13.2",
|
|
||||||
"graphql-tag": "^2.9.2",
|
|
||||||
"moment": "^2.22.2",
|
|
||||||
"pluralize": "^7.0.0",
|
|
||||||
"react": "^16.4.1",
|
|
||||||
"react-apollo": "^2.1.9",
|
|
||||||
"react-dom": "^16.4.1",
|
|
||||||
"react-router": "^4.3.1",
|
|
||||||
"react-router-dom": "^4.3.1",
|
|
||||||
"react-scripts": "1.1.4"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test": "react-scripts test --env=jsdom",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@playlyfe/gql": "^2.6.0"
|
|
||||||
},
|
|
||||||
"proxy": {
|
|
||||||
"/graphql": {
|
|
||||||
"target": "http://10.32.7.1:17000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,40 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
<meta name="theme-color" content="#000000">
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is added to the
|
|
||||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
|
||||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>Aite RP</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
You need to enable JavaScript to run this app.
|
|
||||||
</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Aite RP",
|
|
||||||
"name": "Aite RP Website (aiterp.net)",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": "./index.html",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#000000"
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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 {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See ./common/Menu.js to change the top navbar */
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { ApolloClient } from "apollo-client"
|
|
||||||
import { HttpLink } from "apollo-link-http"
|
|
||||||
import { InMemoryCache } from "apollo-cache-inmemory"
|
|
||||||
|
|
||||||
const client = new ApolloClient({
|
|
||||||
link: new HttpLink({
|
|
||||||
uri: "/graphql"
|
|
||||||
}),
|
|
||||||
|
|
||||||
cache: new InMemoryCache(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export default client
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/* /story/ */
|
|
||||||
.theme-story .color-menu {
|
|
||||||
color: #789;
|
|
||||||
}
|
|
||||||
.theme-story .MenuLink.selected {
|
|
||||||
background-color: rgba(17, 187, 255, 0.125);
|
|
||||||
}
|
|
||||||
.theme-story .MenuLink:hover, .theme-story .Menu > .head-menu > a:hover {
|
|
||||||
background-color: rgba(17, 187, 255, 0.25);
|
|
||||||
}
|
|
||||||
.theme-story .color-primary {
|
|
||||||
color: #4BF;
|
|
||||||
}
|
|
||||||
.theme-story .color-text {
|
|
||||||
color: #ABC;
|
|
||||||
}
|
|
||||||
.theme-story .color-highlight-primary {
|
|
||||||
background-color: rgba(34, 187, 255, 0.25);
|
|
||||||
color: #4BF;
|
|
||||||
}
|
|
||||||
.theme-story .color-highlight-dark {
|
|
||||||
background-color: rgba(0, 9, 18, 0.50);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* /logs/ */
|
|
||||||
.theme-logs .color-menu {
|
|
||||||
color: #998;
|
|
||||||
}
|
|
||||||
.theme-logs .MenuLink.selected {
|
|
||||||
background-color: rgba(255, 187, 17, 0.125);
|
|
||||||
}
|
|
||||||
.theme-logs .MenuLink:hover, .theme-logs .Menu > .head-menu > a:hover {
|
|
||||||
background-color: rgba(255, 187, 17, 0.25);
|
|
||||||
}
|
|
||||||
.theme-logs .color-primary {
|
|
||||||
color: #FB1;
|
|
||||||
}
|
|
||||||
.theme-logs .color-text {
|
|
||||||
color: #CCB;
|
|
||||||
}
|
|
||||||
.theme-logs .color-highlight-primary {
|
|
||||||
background-color: rgba(255, 187, 15, 0.25);
|
|
||||||
color: #FB1;
|
|
||||||
}
|
|
||||||
.theme-logs .color-highlight-dark {
|
|
||||||
background-color: rgba(18, 9, 0, 0.50);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* /data/ */
|
|
||||||
.theme-data .color-menu {
|
|
||||||
color: #898;
|
|
||||||
}
|
|
||||||
.theme-data .MenuLink.selected {
|
|
||||||
background-color: rgba(35, 255, 128, 0.125);
|
|
||||||
}
|
|
||||||
.theme-data .MenuLink:hover, .theme-data .Menu > .head-menu > a:hover {
|
|
||||||
background-color: rgba(35, 255, 128, 0.25);
|
|
||||||
}
|
|
||||||
.theme-data .color-primary {
|
|
||||||
color: #1F8;
|
|
||||||
}
|
|
||||||
.theme-data .color-text {
|
|
||||||
color: #ACB;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tag colors */
|
|
||||||
.color-tag-event {
|
|
||||||
color: #B77;
|
|
||||||
}
|
|
||||||
.color-tag-location {
|
|
||||||
color: #7B7;
|
|
||||||
}
|
|
||||||
.color-tag-organization {
|
|
||||||
color: #B7B;
|
|
||||||
}
|
|
||||||
.color-tag-series {
|
|
||||||
color: #BB7;
|
|
||||||
}
|
|
||||||
.color-tag-character {
|
|
||||||
color: #37B;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Danger */
|
|
||||||
.color-danger {
|
|
||||||
color: #FA3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MenuLink:hover, .MenuLink:hover .text, .Menu > .head-menu > a:hover {
|
|
||||||
color: #EEE !important;
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import "./css/Background.css"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Background renders a full-size image that detects whether the source is
|
|
||||||
* landscape or portrait. It's a react-ification of AiteStory's background.
|
|
||||||
*/
|
|
||||||
export default class Background extends Component {
|
|
||||||
constructor(props, ctx) {
|
|
||||||
super(props, ctx)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
landscape: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {HTMLImageElement} img
|
|
||||||
*/
|
|
||||||
onRef(img) {
|
|
||||||
if (img == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (img.complete && typeof(img.naturalHeight) !== 'undefined' && img.naturalHeight !== 0) {
|
|
||||||
const width = img.naturalWidth
|
|
||||||
const height = img.naturalHeight
|
|
||||||
|
|
||||||
this.setLandscape(width > (height * 1.50))
|
|
||||||
} else {
|
|
||||||
img.onload = () => {
|
|
||||||
const width = img.naturalWidth
|
|
||||||
const height = img.naturalHeight
|
|
||||||
|
|
||||||
this.setLandscape(width > (height * 1.50))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the landscape setting of the image.
|
|
||||||
*
|
|
||||||
* @param {boolean} value Whether the image is landscape
|
|
||||||
*/
|
|
||||||
setLandscape(value) {
|
|
||||||
if (value !== this.state.landscape) {
|
|
||||||
this.setState({landscape: value})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const src = this.props.src || "/assets/images/bg.png"
|
|
||||||
const opacity = this.props.opacity || 0.20
|
|
||||||
const className = "Background" + (this.state.landscape ? " landscape" : "")
|
|
||||||
|
|
||||||
return <img alt="Background" style={{filter: `brightness(${(Number(opacity)) * 100}%)`}} ref={this.onRef.bind(this)} className={className} src={src} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
import moment from "moment"
|
|
||||||
|
|
||||||
import "./css/List.css"
|
|
||||||
|
|
||||||
export default class List extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<table className="List" cellSpacing="0px">
|
|
||||||
<tbody>
|
|
||||||
{this.props.children}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Item({link, name, icon, author, description, tags, fictionalDate, createdDate}) {
|
|
||||||
return [
|
|
||||||
<tr className="ListItem color-primary">
|
|
||||||
<td className="icon color-highlight-primary">{icon}</td>
|
|
||||||
<td className="content color-primary color-highlight-dark">
|
|
||||||
<div className="title">
|
|
||||||
<Link className="color-primary" to={link}>{name}</Link>
|
|
||||||
</div>
|
|
||||||
<div className="description color-text">{description}</div>
|
|
||||||
<div className="meta">
|
|
||||||
<ItemMeta key="D2" kind="date" value={createdDate} />
|
|
||||||
<ItemMeta key="D1" kind="date" value={fictionalDate} />
|
|
||||||
<ItemMeta key="T" kind="tag" value={tags[0] || null} />
|
|
||||||
<ItemMeta key="A" kind="author" value={author} />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>,
|
|
||||||
<tr className="ListItem-spacer">
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function ItemMeta({kind, value, className}) {
|
|
||||||
// Hide empty fields
|
|
||||||
if (value == null || value === "") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case "date": {
|
|
||||||
const m = moment(value)
|
|
||||||
if (m.year() < 2) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="date color-menu">{m.format("MMM D, YYYY")}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
case "tag": {
|
|
||||||
return <div className={`tag color-tag-${value.kind.toLowerCase()}`}>{value.name}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
case "author": {
|
|
||||||
return <div className="author color-menu">{value}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import Background from "./Background"
|
|
||||||
|
|
||||||
import './css/LoadingScreen.css'
|
|
||||||
|
|
||||||
export function LoadingScreen({className}) {
|
|
||||||
return (
|
|
||||||
<div className={`Loading ${className}`}>
|
|
||||||
<Background />
|
|
||||||
<div className="text color-primary">Loading...</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
|
|
||||||
import './css/Main.css'
|
|
||||||
|
|
||||||
export default class Main extends Component {
|
|
||||||
render() {
|
|
||||||
const { children, className } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className={`Main ${className}`}>
|
|
||||||
{ children }
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
|
|
||||||
import './css/Menu.css'
|
|
||||||
|
|
||||||
export default class Menu extends Component {
|
|
||||||
render() {
|
|
||||||
const { children } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className="Menu">
|
|
||||||
<h1 className="color-primary">Aite RP</h1>
|
|
||||||
<div className="head-menu">
|
|
||||||
<HeadMenuLink to="/story/">Story</HeadMenuLink>
|
|
||||||
<HeadMenuLink to="/logs/">Logs</HeadMenuLink>
|
|
||||||
</div>
|
|
||||||
<div className="content">
|
|
||||||
{ children }
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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-${(tagKind||"none").toLowerCase()}`
|
|
||||||
|
|
||||||
const body = (
|
|
||||||
<div className={`MenuLink color-menu ${className}`} onClick={onClick}>
|
|
||||||
<div className="icon color-primary">{icon}</div>
|
|
||||||
<div className={`text ${textClass}`}>{children}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (to == null) {
|
|
||||||
return body
|
|
||||||
} else {
|
|
||||||
return <Link to={to}>{body}</Link>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MenuGap() {
|
|
||||||
return <div className="MenuGap"></div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MenuHeader({children}) {
|
|
||||||
return <div className="MenuHeader color-menu">{children}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HeadMenuLink({to, children}) {
|
|
||||||
const className = (window.location.pathname.startsWith(to) ? "color-primary" : "color-menu")
|
|
||||||
|
|
||||||
return <Link className={className} to={to}>{children}</Link>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MenuBlurb({children}) {
|
|
||||||
return <div className="MenuBlurb">{children}</div>
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
img.Background {
|
|
||||||
/* Set rules to fill background */
|
|
||||||
width: 100%;
|
|
||||||
/* Set up proportionate scaling */
|
|
||||||
min-height: 100%;
|
|
||||||
height: auto;
|
|
||||||
/* Set up positioning */
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: -1;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.Background.landscape {
|
|
||||||
/* Set rules to fill background */
|
|
||||||
height: 100%;
|
|
||||||
/* Set up proportionate scaling */
|
|
||||||
min-width: 100%;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
.List {
|
|
||||||
padding: 1em 1ch;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 140ch;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ListItem {
|
|
||||||
user-select: none;
|
|
||||||
outline: 0.05px solid;
|
|
||||||
}
|
|
||||||
.ListItem td.icon {
|
|
||||||
font-size: 2.75em;
|
|
||||||
text-align: center;
|
|
||||||
width: 2.5ch;
|
|
||||||
outline: 0.05px solid;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.ListItem td.content {
|
|
||||||
padding: 0.25em;
|
|
||||||
padding-bottom: 0.33em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.ListItem td.content .title {
|
|
||||||
padding: 0.25em 1ch;
|
|
||||||
}
|
|
||||||
.ListItem td.content a {
|
|
||||||
font-size: 1.333em;
|
|
||||||
}
|
|
||||||
.ListItem td.content a:hover {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ListItem td.content .description {
|
|
||||||
margin: 0 1ch;
|
|
||||||
}
|
|
||||||
.ListItem td.content p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.ListItem td.content .meta {
|
|
||||||
|
|
||||||
}
|
|
||||||
.ListItem td.content .meta > div {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.25em 1ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ListItem-spacer td {
|
|
||||||
height: 0.75em;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
.Loading .text {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 6em;
|
|
||||||
font-size: 3em;
|
|
||||||
opacity: 0.5;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.Main {
|
|
||||||
margin-left: 30ch;
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
.Menu {
|
|
||||||
position: fixed;
|
|
||||||
width: 28ch;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
text-align: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Menu > .head-menu {
|
|
||||||
padding: 0 2ch;
|
|
||||||
padding-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Menu > .head-menu > a {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.25em 1ch;
|
|
||||||
|
|
||||||
font-size: 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Menu > h1 {
|
|
||||||
padding: 1em 0 0em 0;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 2.5em;
|
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Menu > .content {
|
|
||||||
margin-top: 1em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MenuLink {
|
|
||||||
width: 100%;
|
|
||||||
padding-left: 0.5ch;
|
|
||||||
font-size: 1.1em;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MenuLink .icon {
|
|
||||||
display: inline-block;
|
|
||||||
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 {
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MenuHeader {
|
|
||||||
opacity: 0.5;
|
|
||||||
user-select: none;
|
|
||||||
padding: 0.125em 1.5ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MenuBlurb {
|
|
||||||
opacity: 0.5;
|
|
||||||
padding: 0.25em 1.5ch;
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Less glaring chrome/ium scrollbars.
|
|
||||||
*/
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 2px;
|
|
||||||
/* for vertical scrollbars */
|
|
||||||
height: 2px;
|
|
||||||
/* for horizontal scrollbars */
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-button {
|
|
||||||
background-color: #555;
|
|
||||||
color: 000;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #555;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import { BrowserRouter } from "react-router-dom"
|
|
||||||
import { ApolloProvider } from "react-apollo";
|
|
||||||
|
|
||||||
import client from "./client";
|
|
||||||
import App from "./App"
|
|
||||||
import registerServiceWorker from "./registerServiceWorker"
|
|
||||||
|
|
||||||
import "./index.css"
|
|
||||||
import "./colors.css";
|
|
||||||
|
|
||||||
ReactDOM.render((
|
|
||||||
<ApolloProvider client={client}>
|
|
||||||
<BrowserRouter>
|
|
||||||
<App />
|
|
||||||
</BrowserRouter>
|
|
||||||
</ApolloProvider>
|
|
||||||
), document.getElementById('root'))
|
|
||||||
|
|
||||||
registerServiceWorker();
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
// In production, we register a service worker to serve assets from local cache.
|
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
|
||||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
|
||||||
// cached resources are updated in the background.
|
|
||||||
|
|
||||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
|
||||||
// This link also includes instructions on opting out of this behavior.
|
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
|
||||||
window.location.hostname === 'localhost' ||
|
|
||||||
// [::1] is the IPv6 localhost address.
|
|
||||||
window.location.hostname === '[::1]' ||
|
|
||||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
|
||||||
window.location.hostname.match(
|
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function register() {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://goo.gl/SC7cgQ'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not local host. Just register service worker
|
|
||||||
registerValidSW(swUrl);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register(swUrl)
|
|
||||||
.then(registration => {
|
|
||||||
registration.onupdatefound = () => {
|
|
||||||
const installingWorker = registration.installing;
|
|
||||||
installingWorker.onstatechange = () => {
|
|
||||||
if (installingWorker.state === 'installed') {
|
|
||||||
if (navigator.serviceWorker.controller) {
|
|
||||||
// At this point, the old content will have been purged and
|
|
||||||
// the fresh content will have been added to the cache.
|
|
||||||
// It's the perfect time to display a "New content is
|
|
||||||
// available; please refresh." message in your web app.
|
|
||||||
console.log('New content is available; please refresh.');
|
|
||||||
} else {
|
|
||||||
// At this point, everything has been precached.
|
|
||||||
// It's the perfect time to display a
|
|
||||||
// "Content is cached for offline use." message.
|
|
||||||
console.log('Content is cached for offline use.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error during service worker registration:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl) {
|
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
|
||||||
fetch(swUrl)
|
|
||||||
.then(response => {
|
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
|
||||||
if (
|
|
||||||
response.status === 404 ||
|
|
||||||
response.headers.get('content-type').indexOf('javascript') === -1
|
|
||||||
) {
|
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister().then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Service worker found. Proceed as normal.
|
|
||||||
registerValidSW(swUrl);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregister() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import Menu, { MenuHeader, MenuLink, MenuGap } from '../../common/Menu'
|
|
||||||
|
|
||||||
export default class LogsMenu extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<MenuHeader>Logs</MenuHeader>
|
|
||||||
<MenuLink to="/logs/" icon="R">Recent</MenuLink>
|
|
||||||
<MenuGap />
|
|
||||||
<MenuHeader>Options</MenuHeader>
|
|
||||||
<MenuLink icon="+">Create Log</MenuLink>
|
|
||||||
</Menu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import Background from "../../common/Background"
|
|
||||||
import LogsMenu from "./LogsMenu"
|
|
||||||
|
|
||||||
export default class LogsRoot extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="LogsRoot theme-logs">
|
|
||||||
<Background />
|
|
||||||
<LogsMenu />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
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>
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
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>
|
|
||||||
)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import { Query } from "react-apollo"
|
|
||||||
|
|
||||||
import { LoadingScreen } from "../../common/LoadingScreen"
|
|
||||||
import Main from "../../common/Main"
|
|
||||||
import List, {Item} from "../../common/List"
|
|
||||||
|
|
||||||
import storyListQuery from "./gql/StoryList";
|
|
||||||
|
|
||||||
export class StoryList extends Component {
|
|
||||||
render() {
|
|
||||||
const { stories } = this.props
|
|
||||||
const items = stories.map(story => <Item key={story.id} link={`/story/${story.id}`} icon={story.category.charAt(0)} {...story} />)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Main className="StoryList">
|
|
||||||
<List>{items}</List>
|
|
||||||
</Main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ({filter}) => (
|
|
||||||
<Query query={storyListQuery} variables={{filter}}>
|
|
||||||
{({ loading, error, data }) => {
|
|
||||||
if (loading) {
|
|
||||||
return <LoadingScreen className="theme-story" />
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
return `Error! ${error.message}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return <StoryList {...data} />
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import Menu, { MenuHeader, MenuLink, MenuGap } from "../../common/Menu"
|
|
||||||
import pluralize from "pluralize"
|
|
||||||
|
|
||||||
export default class StoryMenu extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<MenuHeader>Story</MenuHeader>
|
|
||||||
<MenuLink to="/story/" icon="A">All</MenuLink>
|
|
||||||
<MenuLink to="/story/open" icon="O">Open</MenuLink>
|
|
||||||
<MenuGap />
|
|
||||||
<MenuHeader>By Category</MenuHeader>
|
|
||||||
<CategoryLinks categories={this.props.categories} />
|
|
||||||
<MenuGap />
|
|
||||||
<MenuHeader>By Tag</MenuHeader>
|
|
||||||
<MenuLink to="/story/tags/" icon="T">All Tags</MenuLink>
|
|
||||||
<MenuGap />
|
|
||||||
<MenuHeader>Options</MenuHeader>
|
|
||||||
<MenuLink icon="+">Create Story</MenuLink>
|
|
||||||
</Menu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function CategoryLinks({categories}) {
|
|
||||||
return categories.map(category => {
|
|
||||||
const icon = category.charAt(0).toUpperCase()
|
|
||||||
const label = pluralize(category)
|
|
||||||
|
|
||||||
return <MenuLink to={`/story/category/${category}`} icon={icon}>{label}</MenuLink>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import { Route, Switch } from "react-router-dom";
|
|
||||||
import { Query } from "react-apollo"
|
|
||||||
|
|
||||||
import StoryMenu from "./StoryMenu"
|
|
||||||
import Background from "../../common/Background"
|
|
||||||
import { LoadingScreen } from "../../common/LoadingScreen"
|
|
||||||
|
|
||||||
import storyRootQuery from "./gql/StoryRoot"
|
|
||||||
|
|
||||||
import StoryList from "./StoryList";
|
|
||||||
import StoryTags from "./StoryTags";
|
|
||||||
|
|
||||||
export class StoryIndex extends Component {
|
|
||||||
render() {
|
|
||||||
const categories = this.props.categoryType.enumValues.map(ev => ev.name).sort()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="StoryIndex theme-story">
|
|
||||||
<Background />
|
|
||||||
<StoryMenu categories={categories} />
|
|
||||||
<Switch>
|
|
||||||
<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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<Query query={storyRootQuery}>
|
|
||||||
{({ loading, error, data }) => {
|
|
||||||
if (loading) return <LoadingScreen className="theme-story" />
|
|
||||||
if (error) return `Error! ${error.message}`
|
|
||||||
|
|
||||||
return <StoryIndex {...data} />
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import React, { Component } from "react"
|
|
||||||
import { Query } from "react-apollo"
|
|
||||||
|
|
||||||
import { LoadingScreen } from "../../common/LoadingScreen"
|
|
||||||
import Main from "../../common/Main"
|
|
||||||
|
|
||||||
import storyTagsQuery from "./gql/StoryTags";
|
|
||||||
|
|
||||||
export class StoryTags extends Component {
|
|
||||||
render() {
|
|
||||||
const { tags } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Main className="StoryTags">
|
|
||||||
{ tags.map(s => <h1>{s.name}</h1>) }
|
|
||||||
</Main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<Query query={storyTagsQuery}>
|
|
||||||
{({ loading, error, data }) => {
|
|
||||||
if (loading) {
|
|
||||||
return <LoadingScreen className="theme-story" />
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
return `Error! ${error.message}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return <StoryTags {...data} />
|
|
||||||
}}
|
|
||||||
</Query>
|
|
||||||
)
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import gql from "graphql-tag"
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query StoryList($filter: StoriesFilter) {
|
|
||||||
stories(filter: $filter) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
author
|
|
||||||
category
|
|
||||||
tags { kind name }
|
|
||||||
createdDate
|
|
||||||
fictionalDate
|
|
||||||
updatedDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import gql from "graphql-tag"
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query StoryRoot {
|
|
||||||
categoryType: __type(name: "StoryCategory") {
|
|
||||||
enumValues {
|
|
||||||
name
|
|
||||||
description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import gql from "graphql-tag"
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query StoryTags {
|
|
||||||
tags {
|
|
||||||
kind
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
Reference in New Issue
Block a user