diff --git a/frontend/components/Layout/Layout.js b/frontend/components/Layout/Layout.js index 22dc66b93..a49b21b18 100644 --- a/frontend/components/Layout/Layout.js +++ b/frontend/components/Layout/Layout.js @@ -1,7 +1,7 @@ // @flow import React from 'react'; import { withRouter } from 'react-router-dom'; -import Helmet from 'react-helmet'; +import { Helmet } from 'react-helmet'; import styled from 'styled-components'; import { observer, inject } from 'mobx-react'; import keydown from 'react-keydown'; @@ -99,18 +99,15 @@ type Props = { return ( - + + Atlas + + {this.props.ui.progressBarVisible && } - {this.props.notifications} diff --git a/frontend/components/PageTitle/PageTitle.js b/frontend/components/PageTitle/PageTitle.js index 1b3526a89..deb763670 100644 --- a/frontend/components/PageTitle/PageTitle.js +++ b/frontend/components/PageTitle/PageTitle.js @@ -1,11 +1,13 @@ // @flow import React from 'react'; -import Helmet from 'react-helmet'; +import { Helmet } from 'react-helmet'; type Props = { title: string, }; -const PageTitle = ({ title }: Props) => ; +const PageTitle = ({ title }: Props) => ( + {`${title} - Atlas`} +); export default PageTitle; diff --git a/frontend/components/SlackAuthLink/SlackAuthLink.js b/frontend/components/SlackAuthLink/SlackAuthLink.js index d8225c679..3bf9e164f 100644 --- a/frontend/components/SlackAuthLink/SlackAuthLink.js +++ b/frontend/components/SlackAuthLink/SlackAuthLink.js @@ -4,8 +4,8 @@ import { observer, inject } from 'mobx-react'; import AuthStore from 'stores/AuthStore'; type Props = { - children: React.Element, - scopes?: Array, + children: React$Element<*>, + scopes?: string[], auth: AuthStore, redirectUri: string, }; @@ -32,9 +32,7 @@ type Props = { }; const urlParams = Object.keys(params) - .map(key => { - return `${key}=${encodeURIComponent(params[key])}`; - }) + .map(key => `${key}=${encodeURIComponent(params[key])}`) .join('&'); return `${baseUrl}?${urlParams}`; diff --git a/package.json b/package.json index dc878af4e..0206a7219 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ "react-addons-css-transition-group": "15.3.2", "react-dom": "^15.6.1", "react-dropzone": "3.6.0", - "react-helmet": "3.1.0", + "react-helmet": "^5.2.0", "react-keydown": "^1.7.3", "react-modal": "^2.2.1", "react-portal": "^3.1.0", @@ -166,6 +166,7 @@ "string-hash": "^1.1.0", "style-loader": "^0.18.2", "styled-components": "^2.0.0", + "styled-components-grid": "^1.0.0-preview.15", "url-loader": "0.5.7", "uuid": "2.0.2", "validator": "5.2.0", diff --git a/server/api/auth.js b/server/api/auth.js index 8be7d0269..01bc6f1ad 100644 --- a/server/api/auth.js +++ b/server/api/auth.js @@ -50,7 +50,7 @@ router.post('auth.slack', async ctx => { // Signal to backend that the user is logged in. // This is only used to signal SSR rendering, not // used for auth. - ctx.cookies.set('loggedId', 'true', { + ctx.cookies.set('loggedIn', 'true', { httpOnly: false, expires: new Date('2100'), }); diff --git a/server/pages/Home.js b/server/pages/Home.js new file mode 100644 index 000000000..147a93bd7 --- /dev/null +++ b/server/pages/Home.js @@ -0,0 +1,40 @@ +// @flow +import React from 'react'; +import styled from 'styled-components'; +import Grid from 'styled-components-grid'; + +const Unit = Grid.Unit; + +const Header = styled.div` + width: 100%; + padding: 3em; + text-align: center; +`; + +export default function Home() { + return ( + +
+

Your team’s knowledge base

+

+ Documentation, meeting notes, playbooks, onboarding, work logs, brainstorming, decisions, & more… +

+ Sign In +
+ + +

Blazing Fast

+

+ Atlas is fast, really fast. We’ve trimmed 100ms and 50ms there to make sure that documents load instantly, search is speedy and there are keyboard shortcuts for everything. +

+
+ +

Markdown Support

+

+ Documents are stored in Markdown and you can export them at any time. Markdown shortcuts are also built right into the editor so you can easily format using markdown syntax or our GUI. +

+
+ +
+ ); +} diff --git a/server/pages/components/Layout.js b/server/pages/components/Layout.js new file mode 100644 index 000000000..6fe9d56fc --- /dev/null +++ b/server/pages/components/Layout.js @@ -0,0 +1,26 @@ +// @flow +import React from 'react'; +import { Helmet } from 'react-helmet'; + +type Props = { + children?: React$Element<*>, +}; + +export default function Layout({ children }: Props) { + return ( + + + + Atlas + + + + {'{{HEAD}}'} + {'{{CSS}}'} + + + {children} + + + ); +} diff --git a/server/routes.js b/server/routes.js index 2a4c5bc0b..2d94fd097 100644 --- a/server/routes.js +++ b/server/routes.js @@ -1,13 +1,22 @@ +import React from 'react'; import path from 'path'; import fs from 'fs'; import httpErrors from 'http-errors'; import Koa from 'koa'; import Router from 'koa-router'; import sendfile from 'koa-sendfile'; +import ReactDOMServer from 'react-dom/server'; import subdomainRedirect from './middlewares/subdomainRedirect'; +import { Helmet } from 'react-helmet'; +import { ServerStyleSheet, StyleSheetManager } from 'styled-components'; +import Layout from './pages/components/Layout'; +import Home from './pages/Home'; + +const isProduction = process.env.NODE_ENV === 'production'; const koa = new Koa(); const router = new Router(); +const sheet = new ServerStyleSheet(); const readFile = src => { return new Promise((resolve, reject) => { @@ -18,6 +27,25 @@ const readFile = src => { }); }; +const renderPage = children => { + const html = ReactDOMServer.renderToString( + + + {children} + + + ); + + // helmet returns an object of meta tags with toString methods, urgh. + const helmet = Helmet.renderStatic(); + let head = ''; + Object.keys(helmet).forEach(key => (head += helmet[key].toString())); + + return html + .replace('{{CSS}}', sheet.getStyleTags()) + .replace('{{HEAD}}', head); +}; + router.get('/_health', ctx => (ctx.body = 'OK')); if (process.env.NODE_ENV === 'production') { @@ -31,30 +59,32 @@ if (process.env.NODE_ENV === 'production') { path.join(__dirname, '../dist/', ctx.path.substring(8)) ); }); - - router.get('/', async ctx => { - const html = await readFile(path.join(__dirname, '../dist/index.html')); - ctx.body = html; - - if (!ctx.status) ctx.throw(httpErrors.NotFound()); - }); - - router.get('*', async ctx => { - await sendfile(ctx, path.join(__dirname, '../dist/index.html')); - if (!ctx.status) ctx.throw(httpErrors.NotFound()); - }); - - koa.use(subdomainRedirect()); -} else { - router.get('*', async ctx => { - console.log(ctx.cookies.get('loggedIn')); - const html = await readFile(path.join(__dirname, './static/dev.html')); - ctx.body = html; - - if (!ctx.status) ctx.throw(httpErrors.NotFound()); - }); } +router.get('/', async ctx => { + if (ctx.cookies.get('loggedIn')) { + if (isProduction) { + ctx.body = await readFile(path.join(__dirname, '../dist/index.html')); + } else { + ctx.body = await readFile(path.join(__dirname, './static/dev.html')); + } + } else { + ctx.body = await renderPage(); + } + + if (!ctx.status) ctx.throw(httpErrors.NotFound()); +}); + +router.get('*', async ctx => { + if (isProduction) { + ctx.body = await readFile(path.join(__dirname, '../dist/index.html')); + } else { + ctx.body = await readFile(path.join(__dirname, './static/dev.html')); + } + if (!ctx.status) ctx.throw(httpErrors.NotFound()); +}); + +koa.use(subdomainRedirect()); koa.use(router.routes()); // 404 handler diff --git a/server/static/home.html b/server/static/home.html new file mode 100644 index 000000000..35a36c02e --- /dev/null +++ b/server/static/home.html @@ -0,0 +1,64 @@ + + + + Atlas + + + + + +
+

Your team’s knowledge base

+

Documentation, meeting notes, playbooks, onboarding, work logs, brainstorming, decisions, & more…

+ Sign In +
+ +
+
+
+
+

Blazing Fast

+

Atlas is fast, really fast. We’ve trimmed 100ms and 50ms there to make sure that documents load instantly, search is speedy and there are keyboard shortcuts for everything.

+
+
+ +
+
+

Markdown Support

+

Documents are stored in Markdown and you can export them at any time. Markdown shortcuts are also built right into the editor so you can easily format using markdown syntax or our GUI.

+
+
+
+
+ +
+
+
+
+

Beautiful Editor

+

We built a custom editor from the ground up to be great looking, extensible, and a pleasure to use whether you're typing up quick notes or pages of documentation.

+
+
+ +
+

Powerful Search

+

Built-in search makes that one document easy to find in a large knowledgebase.

+
+
+

API & Integrations

+

Atlas is built on it's own API, treat Atlas as a CMS or automatically great documents from outside events.

+
+
+

Open Source

+

Want to contribute or host Atlas yourself? All of the code is on GitHub.

+
+ + + + diff --git a/yarn.lock b/yarn.lock index 3baf998b0..16383256b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2223,7 +2223,7 @@ decompress-response@^3.2.0: dependencies: mimic-response "^1.0.0" -deep-equal@1.0.1, deep-equal@~1.0.1: +deep-equal@^1.0.1, deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2967,7 +2967,7 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -exenv@^1.2.0: +exenv@^1.2.0, exenv@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -3096,14 +3096,6 @@ fbemitter@^2.1.1: dependencies: fbjs "^0.8.4" -fbjs@0.1.0-alpha.10: - version "0.1.0-alpha.10" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.1.0-alpha.10.tgz#46e457c09cbefb51fc752a3e030e7b67fcc384c8" - dependencies: - core-js "^1.0.0" - promise "^7.0.3" - whatwg-fetch "^0.9.0" - fbjs@^0.8.4, fbjs@^0.8.5, fbjs@^0.8.9: version "0.8.14" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" @@ -5538,7 +5530,7 @@ lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" -lodash.keys@^3.0.0, lodash.keys@^3.1.2: +lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" dependencies: @@ -7124,7 +7116,7 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" -promise@7.x, promise@^7.0.3, promise@^7.1.1: +promise@7.x, promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" dependencies: @@ -7291,6 +7283,13 @@ react-addons-test-utils@^15.3.1: version "15.6.0" resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.6.0.tgz#062d36117fe8d18f3ba5e06eb33383b0b85ea5b9" +react-create-component-from-tag-prop@^1.2.1: + version "1.3.1" + resolved "https://registry.npmjs.org/react-create-component-from-tag-prop/-/react-create-component-from-tag-prop-1.3.1.tgz#5389407d99f88ba2b36351780a6094470b44a7c7" + dependencies: + lodash "^4.17.4" + react "^15.5.4" + react-deep-force-update@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c" @@ -7314,15 +7313,14 @@ react-dropzone@3.6.0: dependencies: attr-accept "^1.0.3" -react-helmet@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-3.1.0.tgz#63486194682f33004826f3687dc49a138b557050" +react-helmet@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz#a81811df21313a6d55c5f058c4aeba5d6f3d97a7" dependencies: - deep-equal "1.0.1" - object-assign "^4.0.1" - react-side-effect "1.0.2" - shallowequal "0.2.2" - warning "2.1.0" + deep-equal "^1.0.1" + object-assign "^4.1.1" + prop-types "^15.5.4" + react-side-effect "^1.1.0" react-keydown@^1.7.3: version "1.9.4" @@ -7374,11 +7372,12 @@ react-router@^4.2.0: prop-types "^15.5.4" warning "^3.0.0" -react-side-effect@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.0.2.tgz#98e354decdbf0281e4223d87852d33e345eda561" +react-side-effect@^1.1.0: + version "1.1.3" + resolved "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.3.tgz#512c25abe0dec172834c4001ec5c51e04d41bc5c" dependencies: - fbjs "0.1.0-alpha.10" + exenv "^1.2.1" + shallowequal "^1.0.1" react-test-renderer@^15.3.1: version "15.6.1" @@ -7398,6 +7397,16 @@ react-transform-hmr@^1.0.3: global "^4.3.0" react-proxy "^1.1.7" +react@^15.5.4: + version "15.6.2" + resolved "https://registry.npmjs.org/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" + dependencies: + create-react-class "^15.6.0" + fbjs "^0.8.9" + loose-envify "^1.1.0" + object-assign "^4.1.0" + prop-types "^15.5.10" + react@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" @@ -7999,11 +8008,9 @@ sha.js@^2.4.0, sha.js@^2.4.8: dependencies: inherits "^2.0.1" -shallowequal@0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e" - dependencies: - lodash.keys "^3.1.2" +shallowequal@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f" shebang-command@^1.2.0: version "1.2.0" @@ -8402,6 +8409,18 @@ style-loader@^0.18.2: loader-utils "^1.0.2" schema-utils "^0.3.0" +styled-components-breakpoint@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/styled-components-breakpoint/-/styled-components-breakpoint-1.0.1.tgz#51fb474d9449e228b6f3f2cd232631ec65c149bc" + +styled-components-grid@^1.0.0-preview.15: + version "1.0.0-preview.15" + resolved "https://registry.npmjs.org/styled-components-grid/-/styled-components-grid-1.0.0-preview.15.tgz#90c6a4caa22292f7a2d80f055395f3fa18cd6720" + dependencies: + prop-types "^15.5.8" + react-create-component-from-tag-prop "^1.2.1" + styled-components-breakpoint "^1.0.1" + styled-components@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.1.2.tgz#bb419978e1287c5d0d88fa9106b2dd75f66a324c" @@ -9023,7 +9042,7 @@ walker@~1.0.5: dependencies: makeerror "1.0.x" -warning@2.1.0, warning@^2.0.0: +warning@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" dependencies: @@ -9118,10 +9137,6 @@ whatwg-fetch@>=0.10.0: version "2.0.3" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" -whatwg-fetch@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" - whatwg-url@^4.3.0: version "4.8.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"