Moved Layout to be a wrapper for routes

This commit is contained in:
Jori Lallo
2017-06-24 17:14:36 -07:00
parent aa0ddd94bf
commit b8d0666c63
11 changed files with 211 additions and 222 deletions

View File

@ -154,8 +154,12 @@ const MenuLink = styled(Link)`
`; `;
const Content = styled(Flex)` const Content = styled(Flex)`
height: 100%;
overflow: scroll; overflow: scroll;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 250px;
`; `;
const Sidebar = styled(Flex)` const Sidebar = styled(Flex)`

View File

@ -31,6 +31,8 @@ import Flatpage from 'scenes/Flatpage';
import ErrorAuth from 'scenes/ErrorAuth'; import ErrorAuth from 'scenes/ErrorAuth';
import Error404 from 'scenes/Error404'; import Error404 from 'scenes/Error404';
import Layout from 'components/Layout';
import flatpages from 'static/flatpages'; import flatpages from 'static/flatpages';
let DevTools; let DevTools;
@ -94,33 +96,35 @@ render(
<Route exact path="/auth/error" component={ErrorAuth} /> <Route exact path="/auth/error" component={ErrorAuth} />
<Auth> <Auth>
<Switch> <Layout>
<Route exact path="/dashboard" component={Dashboard} /> <Switch>
<Route exact path="/collections/:id" component={Collection} /> <Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/d/:id" component={Document} /> <Route exact path="/collections/:id" component={Collection} />
<Route exact path="/d/:id" component={Document} />
<Route exact path="/d/:id/:edit" component={Document} /> <Route exact path="/d/:id/:edit" component={Document} />
<Route <Route
exact exact
path="/collections/:id/new" path="/collections/:id/new"
component={DocumentNew} component={DocumentNew}
/> />
<Route exact path="/d/:id/new" component={DocumentNewChild} /> <Route exact path="/d/:id/new" component={DocumentNewChild} />
<Route exact path="/search" component={Search} /> <Route exact path="/search" component={Search} />
<Route exact path="/search/:query" component={Search} /> <Route exact path="/search/:query" component={Search} />
<Route exact path="/settings" component={Settings} /> <Route exact path="/settings" component={Settings} />
<Route <Route
exact exact
path="/keyboard-shortcuts" path="/keyboard-shortcuts"
component={KeyboardShortcuts} component={KeyboardShortcuts}
/> />
<Route exact path="/developers" component={Api} /> <Route exact path="/developers" component={Api} />
<Route path="/404" component={Error404} /> <Route path="/404" component={Error404} />
<Route component={notFoundSearch} /> <Route component={notFoundSearch} />
</Switch> </Switch>
</Layout>
</Auth> </Auth>
</Switch> </Switch>
</Router> </Router>

View File

@ -7,7 +7,6 @@ import _ from 'lodash';
import CollectionsStore from 'stores/CollectionsStore'; import CollectionsStore from 'stores/CollectionsStore';
import CollectionStore from './CollectionStore'; import CollectionStore from './CollectionStore';
import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import PreviewLoading from 'components/PreviewLoading'; import PreviewLoading from 'components/PreviewLoading';
@ -31,15 +30,11 @@ type Props = {
}; };
render() { render() {
return ( return this.store.redirectUrl
<Layout> ? <Redirect to={this.store.redirectUrl} />
{this.store.redirectUrl : <CenteredContent>
? <Redirect to={this.store.redirectUrl} /> <PreviewLoading />
: <CenteredContent> </CenteredContent>;
<PreviewLoading />
</CenteredContent>}
</Layout>
);
} }
} }
export default inject('collections')(Collection); export default inject('collections')(Collection);

View File

@ -5,7 +5,6 @@ import { Flex } from 'reflexbox';
import CollectionsStore from 'stores/CollectionsStore'; import CollectionsStore from 'stores/CollectionsStore';
import Layout from 'components/Layout';
import Collection from 'components/Collection'; import Collection from 'components/Collection';
import PreviewLoading from 'components/PreviewLoading'; import PreviewLoading from 'components/PreviewLoading';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
@ -21,17 +20,15 @@ type Props = {
const { collections } = this.props; const { collections } = this.props;
return ( return (
<Layout> <CenteredContent>
<CenteredContent> <Flex column auto>
<Flex column auto> {!collections.isLoaded
{!collections.isLoaded ? <PreviewLoading />
? <PreviewLoading /> : collections.data.map(collection => (
: collections.data.map(collection => ( <Collection key={collection.id} data={collection} />
<Collection key={collection.id} data={collection} /> ))}
))} </Flex>
</Flex> </CenteredContent>
</CenteredContent>
</Layout>
); );
} }
} }

View File

@ -9,10 +9,9 @@ import { Flex } from 'reflexbox';
import UiStore from 'stores/UiStore'; import UiStore from 'stores/UiStore';
import DocumentStore from './DocumentStore'; import DocumentStore from './DocumentStore';
import Breadcrumbs from './components/Breadcrumbs'; // import Menu from './components/Menu';
import Menu from './components/Menu';
import Editor from 'components/Editor'; import Editor from 'components/Editor';
import Layout, { HeaderAction, SaveAction } from 'components/Layout'; // import { HeaderAction, SaveAction } from 'components/Layout';
import PublishingInfo from 'components/PublishingInfo'; import PublishingInfo from 'components/PublishingInfo';
import PreviewLoading from 'components/PreviewLoading'; import PreviewLoading from 'components/PreviewLoading';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
@ -47,9 +46,11 @@ type Props = {
if (this.props.newDocument) { if (this.props.newDocument) {
this.store.collectionId = this.props.match.params.id; this.store.collectionId = this.props.match.params.id;
this.store.newDocument = true; this.store.newDocument = true;
this.props.ui.enableEditMode();
} else if (this.props.match.params.edit) { } else if (this.props.match.params.edit) {
this.store.documentId = this.props.match.params.id; this.store.documentId = this.props.match.params.id;
this.store.fetchDocument(); this.store.fetchDocument();
this.props.ui.enableEditMode();
} else if (this.props.newChildDocument) { } else if (this.props.newChildDocument) {
this.store.documentId = this.props.match.params.id; this.store.documentId = this.props.match.params.id;
this.store.newChildDocument = true; this.store.newChildDocument = true;
@ -76,6 +77,7 @@ type Props = {
} else { } else {
this.store.updateDocument(options); this.store.updateDocument(options);
} }
this.props.ui.disableEditMode();
}; };
onImageUploadStart = () => { onImageUploadStart = () => {
@ -91,18 +93,18 @@ type Props = {
}; };
render() { render() {
const isNew = this.props.newDocument || this.props.newChildDocument; // const isNew = this.props.newDocument || this.props.newChildDocument;
const isEditing = this.props.match.params.edit; const isEditing = this.props.match.params.edit;
const title = ( /*const title = (
<Breadcrumbs <Breadcrumbs
document={this.store.document} document={this.store.document}
pathToDocument={this.store.pathToDocument} pathToDocument={this.store.pathToDocument}
/> />
); );*/
const titleText = this.store.document && get(this.store, 'document.title'); const titleText = this.store.document && get(this.store, 'document.title');
const actions = ( /*const actions = (
<Flex> <Flex>
<HeaderAction> <HeaderAction>
{isEditing {isEditing
@ -115,48 +117,43 @@ type Props = {
</HeaderAction> </HeaderAction>
<Menu store={this.store} document={this.store.document} /> <Menu store={this.store} document={this.store.document} />
</Flex> </Flex>
); );*/
// actions={actions}
// title={title}
// loading={this.store.isSaving || this.store.isUploading}
// search={false}
// fixed
return ( return (
<Layout <PagePadding>
actions={actions} <PageTitle title={titleText} />
title={title} <Prompt when={this.store.hasPendingChanges} message={DISCARD_CHANGES} />
loading={this.store.isSaving || this.store.isUploading} {this.store.isFetching &&
search={false} <CenteredContent>
fixed <PreviewLoading />
> </CenteredContent>}
<PagePadding> {this.store.document &&
<PageTitle title={titleText} /> <Container>
<Prompt {!isEditing &&
when={this.store.hasPendingChanges} <PublishingInfo
message={DISCARD_CHANGES} collaborators={this.store.document.collaborators}
/> createdAt={this.store.document.createdAt}
{this.store.isFetching && createdBy={this.store.document.createdBy}
<CenteredContent> updatedAt={this.store.document.updatedAt}
<PreviewLoading /> updatedBy={this.store.document.updatedBy}
</CenteredContent>} />}
{this.store.document && <Editor
<Container> text={this.store.document.text}
{!isEditing && onImageUploadStart={this.onImageUploadStart}
<PublishingInfo onImageUploadStop={this.onImageUploadStop}
collaborators={this.store.document.collaborators} onChange={this.store.updateText}
createdAt={this.store.document.createdAt} onSave={this.onSave}
createdBy={this.store.document.createdBy} onCancel={this.onCancel}
updatedAt={this.store.document.updatedAt} readOnly={!isEditing}
updatedBy={this.store.document.updatedBy} />
/>} </Container>}
<Editor </PagePadding>
text={this.store.document.text}
onImageUploadStart={this.onImageUploadStart}
onImageUploadStop={this.onImageUploadStop}
onChange={this.store.updateText}
onSave={this.onSave}
onCancel={this.onCancel}
readOnly={!isEditing}
/>
</Container>}
</PagePadding>
</Layout>
); );
} }
} }

View File

@ -2,23 +2,20 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
class Error404 extends React.Component { class Error404 extends React.Component {
render() { render() {
return ( return (
<Layout> <CenteredContent>
<PageTitle title="Not found" /> <PageTitle title="Not found" />
<CenteredContent> <h1>Not Found</h1>
<h1>Not Found</h1>
<p>We're unable to find the page you're accessing.</p> <p>We're unable to find the page you're accessing.</p>
<p>Maybe you want to try <Link to="/search">search</Link> instead?</p> <p>Maybe you want to try <Link to="/search">search</Link> instead?</p>
</CenteredContent> </CenteredContent>
</Layout>
); );
} }
} }

View File

@ -2,23 +2,20 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
class ErrorAuth extends React.Component { class ErrorAuth extends React.Component {
render() { render() {
return ( return (
<Layout> <CenteredContent>
<PageTitle title="Authentication error" /> <PageTitle title="Authentication error" />
<CenteredContent> <h1>Authentication failed</h1>
<h1>Authentication failed</h1>
<p> <p>
We were unable to log you in. <Link to="/">Please try again.</Link> We were unable to log you in. <Link to="/">Please try again.</Link>
</p> </p>
</CenteredContent> </CenteredContent>
</Layout>
); );
} }
} }

View File

@ -7,7 +7,6 @@ import styled from 'styled-components';
import AuthStore from 'stores/AuthStore'; import AuthStore from 'stores/AuthStore';
import Layout from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import SlackAuthLink from 'components/SlackAuthLink'; import SlackAuthLink from 'components/SlackAuthLink';
import Alert from 'components/Alert'; import Alert from 'components/Alert';
@ -40,30 +39,28 @@ type Props = {
return ( return (
<Flex auto> <Flex auto>
<Layout notifications={this.notifications}> {this.props.auth.authenticated && <Redirect to="/dashboard" />}
{this.props.auth.authenticated && <Redirect to="/dashboard" />}
<CenteredContent> <CenteredContent>
{showLandingPageCopy && {showLandingPageCopy &&
<div>
<Title>Simple, fast, markdown.</Title>
<Copy>
We're building a modern wiki for engineering teams.
</Copy>
</div>}
<div> <div>
<SlackAuthLink redirectUri={`${BASE_URL}/auth/slack`}> <Title>Simple, fast, markdown.</Title>
<img <Copy>
alt="Sign in with Slack" We're building a modern wiki for engineering teams.
height="40" </Copy>
width="172" </div>}
src="https://platform.slack-edge.com/img/sign_in_with_slack.png" <div>
srcSet="https://platform.slack-edge.com/img/sign_in_with_slack.png 1x, https://platform.slack-edge.com/img/sign_in_with_slack@2x.png 2x" <SlackAuthLink redirectUri={`${BASE_URL}/auth/slack`}>
/> <img
</SlackAuthLink> alt="Sign in with Slack"
</div> height="40"
</CenteredContent> width="172"
</Layout> src="https://platform.slack-edge.com/img/sign_in_with_slack.png"
srcSet="https://platform.slack-edge.com/img/sign_in_with_slack.png 1x, https://platform.slack-edge.com/img/sign_in_with_slack@2x.png 2x"
/>
</SlackAuthLink>
</div>
</CenteredContent>
</Flex> </Flex>
); );
} }

View File

@ -12,7 +12,6 @@ import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
import SearchField from './components/SearchField'; import SearchField from './components/SearchField';
import SearchStore from './SearchStore'; import SearchStore from './SearchStore';
import Layout, { Title } from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import DocumentPreview from 'components/DocumentPreview'; import DocumentPreview from 'components/DocumentPreview';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
@ -83,41 +82,38 @@ const ResultsWrapper = styled(Flex)`
render() { render() {
const query = this.props.match.params.query; const query = this.props.match.params.query;
const title = <Title content="Search" />;
const hasResults = this.store.documents.length > 0; const hasResults = this.store.documents.length > 0;
return ( return (
<Layout title={title} search={false} loading={this.store.isFetching}> <Container auto>
<PageTitle title="Search" /> <PageTitle title="Search" />
<Container auto> {this.props.notFound &&
{this.props.notFound && <div>
<div> <h1>Not Found</h1>
<h1>Not Found</h1> <p>We're unable to find the page you're accessing.</p>
<p>We're unable to find the page you're accessing.</p> <hr />
<hr /> </div>}
</div>} <ResultsWrapper pinToTop={hasResults} column auto>
<ResultsWrapper pinToTop={hasResults} column auto> <SearchField
<SearchField searchTerm={this.store.searchTerm}
searchTerm={this.store.searchTerm} onKeyDown={this.handleKeyDown}
onKeyDown={this.handleKeyDown} onChange={this.updateQuery}
onChange={this.updateQuery} value={query || ''}
value={query || ''} />
/> <ArrowKeyNavigation
<ArrowKeyNavigation mode={ArrowKeyNavigation.mode.VERTICAL}
mode={ArrowKeyNavigation.mode.VERTICAL} defaultActiveChildIndex={0}
defaultActiveChildIndex={0} >
> {this.store.documents.map((document, index) => (
{this.store.documents.map((document, index) => ( <DocumentPreview
<DocumentPreview innerRef={ref => index === 0 && this.setFirstDocumentRef(ref)}
innerRef={ref => index === 0 && this.setFirstDocumentRef(ref)} key={document.id}
key={document.id} document={document}
document={document} />
/> ))}
))} </ArrowKeyNavigation>
</ArrowKeyNavigation> </ResultsWrapper>
</ResultsWrapper> </Container>
</Container>
</Layout>
); );
} }
} }

View File

@ -8,7 +8,6 @@ import ApiKeyRow from './components/ApiKeyRow';
import styles from './Settings.scss'; import styles from './Settings.scss';
import SettingsStore from './SettingsStore'; import SettingsStore from './SettingsStore';
import Layout, { Title } from 'components/Layout';
import CenteredContent from 'components/CenteredContent'; import CenteredContent from 'components/CenteredContent';
import SlackAuthLink from 'components/SlackAuthLink'; import SlackAuthLink from 'components/SlackAuthLink';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
@ -22,72 +21,69 @@ import PageTitle from 'components/PageTitle';
} }
render() { render() {
const title = <Title content="Settings" />;
const showSlackSettings = DEPLOYMENT === 'hosted'; const showSlackSettings = DEPLOYMENT === 'hosted';
return ( return (
<Layout title={title} search={false} loading={this.store.isFetching}> <CenteredContent>
<PageTitle title="Settings" /> <PageTitle title="Settings" />
<CenteredContent> {showSlackSettings &&
{showSlackSettings &&
<div className={styles.section}>
<h2 className={styles.sectionHeader}>Slack</h2>
<p>
Connect Atlas to your Slack to instantly search for your documents
using <code>/atlas</code> command.
</p>
<SlackAuthLink
scopes={['commands']}
redirectUri={`${BASE_URL}/auth/slack/commands`}
>
<img
alt="Add to Slack"
height="40"
width="139"
src="https://platform.slack-edge.com/img/add_to_slack.png"
srcSet="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x"
/>
</SlackAuthLink>
</div>}
<div className={styles.section}> <div className={styles.section}>
<h2 className={styles.sectionHeader}>API access</h2> <h2 className={styles.sectionHeader}>Slack</h2>
<p> <p>
Create API tokens to hack on your Atlas. Connect Atlas to your Slack to instantly search for your documents
Learn more in <a href>API documentation</a>. using <code>/atlas</code> command.
</p> </p>
{this.store.apiKeys && <SlackAuthLink
<table className={styles.apiKeyTable}> scopes={['commands']}
<tbody> redirectUri={`${BASE_URL}/auth/slack/commands`}
{this.store.apiKeys && >
this.store.apiKeys.map(key => ( <img
<ApiKeyRow alt="Add to Slack"
id={key.id} height="40"
key={key.id} width="139"
name={key.name} src="https://platform.slack-edge.com/img/add_to_slack.png"
secret={key.secret} srcSet="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x"
onDelete={this.store.deleteApiKey}
/>
))}
</tbody>
</table>}
<div>
<InlineForm
placeholder="Token name"
buttonLabel="Create token"
name="inline_form"
value={this.store.keyName}
onChange={this.store.setKeyName}
onSubmit={this.store.createApiKey}
disabled={this.store.isFetching}
/> />
</div> </SlackAuthLink>
</div>}
<div className={styles.section}>
<h2 className={styles.sectionHeader}>API access</h2>
<p>
Create API tokens to hack on your Atlas.
Learn more in <a href>API documentation</a>.
</p>
{this.store.apiKeys &&
<table className={styles.apiKeyTable}>
<tbody>
{this.store.apiKeys &&
this.store.apiKeys.map(key => (
<ApiKeyRow
id={key.id}
key={key.id}
name={key.name}
secret={key.secret}
onDelete={this.store.deleteApiKey}
/>
))}
</tbody>
</table>}
<div>
<InlineForm
placeholder="Token name"
buttonLabel="Create token"
name="inline_form"
value={this.store.keyName}
onChange={this.store.setKeyName}
onSubmit={this.store.createApiKey}
disabled={this.store.isFetching}
/>
</div> </div>
</CenteredContent> </div>
</Layout> </CenteredContent>
); );
} }
} }

View File

@ -3,6 +3,7 @@ import { observable, action } from 'mobx';
class UiStore { class UiStore {
@observable activeCollection: ?string; @observable activeCollection: ?string;
@observable editMode: boolean = false;
/* Actions */ /* Actions */
@ -13,6 +14,14 @@ class UiStore {
@action clearActiveCollection = (): void => { @action clearActiveCollection = (): void => {
this.activeCollection = null; this.activeCollection = null;
}; };
@action enableEditMode() {
this.editMode = true;
}
@action disableEditMode() {
this.editMode = false;
}
} }
export default UiStore; export default UiStore;