diff --git a/iris/index.js b/iris/index.js
index 7745a9d511..f7e64573d3 100644
--- a/iris/index.js
+++ b/iris/index.js
@@ -25,10 +25,6 @@ const PORT = 3001;
initPassport();
// API server
const app = express();
-// $FlowFixMe
-if (IS_PROD) {
- require('newrelic');
-}
import middlewares from './routes/middlewares';
app.use(middlewares);
diff --git a/iris/newrelic.js b/iris/newrelic.js
deleted file mode 100644
index 14be5b490c..0000000000
--- a/iris/newrelic.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-/**
- * New Relic agent configuration.
- *
- * See lib/config.defaults.js in the agent distribution for a more complete
- * description of configuration variables and their potential values.
- */
-exports.config = {
- /**
- * Array of application names.
- */
- app_name: ['Spectrum'],
- /**
- * Your New Relic license key.
- */
- license_key: 'aeeaaf8d4e70e0e859cc3a4d000fb13b7b319b01',
- logging: {
- /**
- * Level at which to log. 'trace' is most useful to New Relic when diagnosing
- * issues with the agent, 'info' and higher will impose the least overhead on
- * production applications.
- */
- level: 'info',
- },
-};
diff --git a/iris/routes/auth/logout.js b/iris/routes/auth/logout.js
index a2fc943579..1c90273585 100644
--- a/iris/routes/auth/logout.js
+++ b/iris/routes/auth/logout.js
@@ -1,5 +1,6 @@
// @flow
import { Router } from 'express';
+const debug = require('debug')('iris:routes:auth:logout');
import { destroySession } from '../../models/session';
const IS_PROD = process.env.NODE_ENV === 'production';
@@ -7,24 +8,19 @@ const HOME = IS_PROD ? '/' : 'http://localhost:3000/';
const logoutRouter = Router();
logoutRouter.get('/', (req, res) => {
+ debug('started');
const sessionCookie = req.cookies['connect.sid'];
if (req.isUnauthenticated() || !sessionCookie) {
+ debug('is unauthenticated, aborting logout');
return res.redirect(HOME);
}
- const sessionId = sessionCookie.split('.')[0].replace('s:', '');
- return destroySession(sessionId)
- .then(() => {
- // I should not have to do this manually
- // but it doesn't work otherwise ¯\_(ツ)_/¯
- res.clearCookie('connect.sid');
- req.logout();
- res.redirect(HOME);
- })
- .catch(err => {
- res.clearCookie('connect.sid');
- console.log(err);
- res.redirect(HOME);
- });
+ debug('logging out');
+ req.logout();
+ req.session.destroy(err => {
+ if (err) console.log(err);
+ debug(`destroyed session, redirecting`);
+ res.redirect(HOME);
+ });
});
export default logoutRouter;
diff --git a/iris/routes/middlewares/index.js b/iris/routes/middlewares/index.js
index f1a7aeaa7e..319dae43b4 100644
--- a/iris/routes/middlewares/index.js
+++ b/iris/routes/middlewares/index.js
@@ -3,6 +3,11 @@ import { Router } from 'express';
const middlewares = Router();
+if (process.env.NODE_ENV === 'development') {
+ const logging = require('./logging');
+ middlewares.use(logging);
+}
+
if (process.env.NODE_ENV === 'production' && !process.env.FORCE_DEV) {
// Raven (Sentry client) needs to come before everything else
const raven = require('./raven').default;
diff --git a/iris/routes/middlewares/logging.js b/iris/routes/middlewares/logging.js
new file mode 100644
index 0000000000..edffceb546
--- /dev/null
+++ b/iris/routes/middlewares/logging.js
@@ -0,0 +1,8 @@
+// @flow
+// Log requests with debug
+const debug = require('debug')('iris:web');
+
+module.exports = (req: Request, res: Response, next: Function) => {
+ debug(`requesting ${req.url}`);
+ next();
+};
diff --git a/iris/routes/middlewares/session.js b/iris/routes/middlewares/session.js
index 369e237e59..4e884da2b0 100644
--- a/iris/routes/middlewares/session.js
+++ b/iris/routes/middlewares/session.js
@@ -14,7 +14,7 @@ export default session({
// Forces a session that is "uninitialized" to be saved to the store
// NOTE(@mxstbr): This might not be necessary or even useful, but the default example of
// session-rethinkdb uses it. Ref: llambda/session-rethinkdb#12
- saveUninitialized: true,
+ saveUninitialized: false,
// Force a session identifier cookie to be set on every response, resets the expire date of the
// cookie to one year from the time of the response, meaning you'll only get logged out after a
// year of inactivity.
diff --git a/package.json b/package.json
index d1c014218b..74c8260262 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"flow-bin": "^0.43.0",
"lint-staged": "^3.3.0",
"micromatch": "^3.0.4",
+ "newrelic": "^2.2.0",
"nodemon": "^1.11.0",
"prettier": "^1.0.0",
"raw-loader": "^0.5.1",
diff --git a/public/img/conversation.svg b/public/img/conversation.svg
new file mode 100644
index 0000000000..9693b2d946
--- /dev/null
+++ b/public/img/conversation.svg
@@ -0,0 +1,162 @@
+
diff --git a/public/img/discover.png b/public/img/discover.png
new file mode 100644
index 0000000000..ccf7127be4
Binary files /dev/null and b/public/img/discover.png differ
diff --git a/public/img/goopy-2.svg b/public/img/goopy-2.svg
deleted file mode 100644
index 185d6f0592..0000000000
--- a/public/img/goopy-2.svg
+++ /dev/null
@@ -1,37 +0,0 @@
-
diff --git a/public/img/goopy-3.svg b/public/img/goopy-3.svg
deleted file mode 100644
index 09cb229028..0000000000
--- a/public/img/goopy-3.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/public/img/goopy-4.svg b/public/img/goopy-4.svg
deleted file mode 100644
index e1fffe4687..0000000000
--- a/public/img/goopy-4.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
diff --git a/public/img/goopy.svg b/public/img/goopy.svg
deleted file mode 100755
index 98bfec8567..0000000000
--- a/public/img/goopy.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
diff --git a/src/actions/authentication.js b/src/actions/authentication.js
index 5a4fba152c..28fb594b48 100644
--- a/src/actions/authentication.js
+++ b/src/actions/authentication.js
@@ -4,7 +4,7 @@ import { clearApolloStore } from '../api';
import { removeItemFromStorage, storeItem } from '../helpers/localStorage';
import Raven from 'raven-js';
-export const logout = () => {
+export const logout = dispatch => {
track(`user`, `sign out`, null);
// clear localStorage
removeItemFromStorage('spectrum');
@@ -15,6 +15,10 @@ export const logout = () => {
process.env.NODE_ENV === 'production'
? '/auth/logout'
: 'http://localhost:3001/auth/logout';
+
+ dispatch({
+ type: 'CLEAR_USER',
+ });
};
export const saveUserDataToLocalStorage = (user: Object) => dispatch => {
diff --git a/src/api/index.js b/src/api/index.js
index dd49dfbf25..41bd03b672 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -101,6 +101,11 @@ export const SERVER_URL =
? `${window.location.protocol}//${window.location.host}`
: 'http://localhost:3001';
+export const CLIENT_URL =
+ process.env.NODE_ENV === 'production'
+ ? `${window.location.protocol}//${window.location.host}`
+ : 'http://localhost:3000';
+
export const PUBLIC_STRIPE_KEY =
process.env.NODE_ENV === 'production'
? 'pk_live_viV7X5XXD1sw8aN2NgQjiff6'
diff --git a/src/components/buttons/style.js b/src/components/buttons/style.js
index af16a4e040..bee70baae7 100644
--- a/src/components/buttons/style.js
+++ b/src/components/buttons/style.js
@@ -20,7 +20,7 @@ const baseButton = css`
line-height: 1;
position: relative;
text-align: center;
- padding: ${props => (props.icon ? '4px 8px 4px 4px' : '12px 16px')};
+ padding: ${props => (props.icon ? '4px 8px' : '12px 16px')};
&:hover {
transition: ${Transition.hover.on};
diff --git a/src/components/fullscreenView/index.js b/src/components/fullscreenView/index.js
index 22f5ea1371..b5639ac4f9 100644
--- a/src/components/fullscreenView/index.js
+++ b/src/components/fullscreenView/index.js
@@ -5,7 +5,7 @@ import {
ClusterTwo,
ClusterThree,
ClusterFour,
-} from '../../views/homepage/style';
+} from '../../views/splash/components/illustrations';
import Icon from '../../components/icons';
import { FullscreenViewContainer, Illustrations, Close } from './style';
diff --git a/src/components/goop/index.js b/src/components/goop/index.js
new file mode 100644
index 0000000000..5c3c33c9c1
--- /dev/null
+++ b/src/components/goop/index.js
@@ -0,0 +1,128 @@
+import React from 'react';
+import styled, { css } from 'styled-components';
+import { zIndex } from '../globals';
+
+/* eslint no-eval: 0 */
+
+export const InlineSvg = styled.svg`
+ position: absolute;
+ top: auto;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ color: inherit;
+ fill: currentColor;
+
+ > g {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ top: auto;
+ }
+`;
+
+export const SvgWrapper = styled.div`
+ position: relative;
+ flex: none;
+ z-index: ${zIndex.base};
+ height: 80px;
+ width: 110%;
+ bottom: -4px;
+ left: -5%;
+ right: -5%;
+ display: ${props => (props.goop === 0 ? 'none' : 'inline-block')};
+ color: ${props => eval(`props.theme.${props.color}`)};
+
+ @media (max-width: 768px) {
+ width: 150%;
+ left: -25%;
+ right: -25%;
+ }
+`;
+
+class Goop extends React.Component {
+ returnGoop() {
+ switch (this.props.goop) {
+ default:
+ case 0:
+ return null;
+ case 1:
+ return (
+
+
+
+ );
+ case 2:
+ return (
+
+
+
+ );
+ case 3:
+ return (
+
+
+
+ );
+ case 4:
+ return (
+
+
+
+ );
+ case 5:
+ return (
+
+
+
+
+ );
+ case 6:
+ return (
+
+
+
+
+
+ );
+ case 7:
+ return (
+
+
+
+
+ );
+ }
+ }
+
+ render() {
+ return (
+
+
+ goop
+ {this.returnGoop()}
+
+
+ );
+ }
+}
+
+Goop.defaultProps = {
+ color: 'bg.default',
+};
+
+export default Goop;
diff --git a/src/components/linkPreview/index.js b/src/components/linkPreview/index.js
index d2b1ba5d26..61b6ef7972 100644
--- a/src/components/linkPreview/index.js
+++ b/src/components/linkPreview/index.js
@@ -8,7 +8,6 @@ import {
LinkPreviewImage,
LinkPreviewTextContainer,
MetaTitle,
- MetaDescription,
MetaUrl,
Close,
LinkPreviewSkeleton,
@@ -30,12 +29,7 @@ export class LinkPreview extends Component {
};
render() {
- let {
- data: { description, image, title, url, trueUrl },
- editable,
- margin,
- } = this.props;
- description = description ? truncate(description, 80) : '';
+ let { data: { image, title, url, trueUrl }, editable, margin } = this.props;
title = title ? truncate(title, 72) : '';
return (
diff --git a/src/components/logo/index.js b/src/components/logo/index.js
index 17d7225503..9093ee5d2b 100644
--- a/src/components/logo/index.js
+++ b/src/components/logo/index.js
@@ -3,37 +3,56 @@ import React from 'react';
//$FlowFixMe
import styled from 'styled-components';
-const Svg = styled.svg`
- fill: ${props => (props.color ? props.color : props.theme.text.reverse)};
- max-width: 100%;
+export const Svg = styled.svg`
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ color: inherit;
+ fill: currentColor;
+`;
+
+export const SvgWrapper = styled.div`
+ display: inline-block;
+ flex: none;
+ width: 160px;
+ height: 24px;
+ top: 4px;
+ position: relative;
+ color: inherit;
`;
export const Logo = () => {
return (
-
+
+
);
};
diff --git a/src/components/upsell/index.js b/src/components/upsell/index.js
index e8a1ff84e2..53bbe6b2ec 100644
--- a/src/components/upsell/index.js
+++ b/src/components/upsell/index.js
@@ -15,6 +15,7 @@ import { openModal } from '../../actions/modals';
import { Avatar } from '../avatar';
import Card from '../card';
import { Button, OutlineButton } from '../buttons';
+import { Login } from '../../views/login';
import {
Title,
MiniTitle,
@@ -178,111 +179,7 @@ export class UpsellSignIn extends Component {
const preferredSigninMethod = getItemFromStorage('preferred_signin_method');
if (isSigningIn) {
- const title =
- signinType === 'signup' ? 'Good times ahead!' : 'Welcome back!';
- const subtitle =
- signinType === 'signup'
- ? 'Spectrum is a place where communities can share, discuss, and grow together. Sign in below to get in on the conversation.'
- : "We're happy to see you again - log in below to get back into the conversation!";
- const verb = signinType === 'signup' ? 'Sign up' : 'Log in';
-
- return (
-
-
-
-
-
-
- {title}
-
-
- {subtitle}
-
-
-
- {preferredSigninMethod &&
-
- this.trackSignin('secondary cta', 'twitter')}
- >
- {verb} with Twitter
-
-
-
- this.trackSignin('secondary cta', 'facebook')}
- >
- {verb} with Facebook
-
-
- this.trackSignin('secondary cta', 'google')}
- >
- {verb} with Google
-
- }
-
- {!preferredSigninMethod &&
-
- this.trackSignin('secondary cta', 'twitter')}
- >
- {verb} with Twitter
-
-
-
- this.trackSignin('secondary cta', 'facebook')}
- >
- {verb} with Facebook
-
-
- this.trackSignin('secondary cta', 'google')}
- >
- {verb} with Google
-
- }
-
-
-
- By using Spectrum, you agree to our{' '}
-
- Code of Conduct
-
-
-
-
- );
+ return ;
} else {
const subtitle = view
? view.type === 'community'
diff --git a/src/helpers/signed-out-fallback.js b/src/helpers/signed-out-fallback.js
new file mode 100644
index 0000000000..d3592c551e
--- /dev/null
+++ b/src/helpers/signed-out-fallback.js
@@ -0,0 +1,32 @@
+// @flow
+// Render a component depending on a users authentication status
+import React from 'react';
+// @FlowFixMe
+import { connect } from 'react-redux';
+
+// This is the component that determines at render time what to do
+const Switch = props => {
+ const { Component, FallbackComponent, currentUser, ...rest } = props;
+
+ if (!currentUser || !Component) {
+ return ;
+ } else {
+ return ;
+ }
+};
+
+// Connect that component to the Redux state
+const ConnectedSwitch = connect(state => ({
+ currentUser: state.users.currentUser,
+}))(Switch);
+
+const signedOutFallback = (Component, FallbackComponent) => {
+ return props =>
+ ;
+};
+
+export default signedOutFallback;
diff --git a/src/index.js b/src/index.js
index 064509c762..045e83f53c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -5,14 +5,16 @@ import ReactDOM from 'react-dom';
import { ThemeProvider } from 'styled-components';
//$FlowFixMe
import { ApolloProvider } from 'react-apollo';
-import queryString from 'query-string';
+//$FlowFixMe
+import { Router } from 'react-router';
import { history } from './helpers/history';
+import queryString from 'query-string';
import { client } from './api';
import { initStore } from './store';
import { getItemFromStorage } from './helpers/localStorage';
import { theme } from './components/theme';
import Routes from './routes';
-import Homepage from './views/homepage';
+import Splash from './views/splash';
import { addToastWithTimeout } from './actions/toasts';
import registerServiceWorker from './registerServiceWorker';
import type { ServiceWorkerResult } from './registerServiceWorker';
@@ -41,30 +43,16 @@ if (existingUser) {
}
function render() {
- // if user is not stored in localStorage and they visit a blacklist url
- if (
- (!existingUser || existingUser === null) &&
- (window.location.pathname === '/' ||
- window.location.pathname === '/messages' ||
- window.location.pathname === '/messages/new' ||
- window.location.pathname === '/notifications')
- ) {
- return ReactDOM.render(
-
-
- ,
- document.querySelector('#root')
- );
- } else {
- return ReactDOM.render(
+ return ReactDOM.render(
+
- ,
- document.querySelector('#root')
- );
- }
+
+ ,
+ document.querySelector('#root')
+ );
}
try {
diff --git a/src/reducers/users.js b/src/reducers/users.js
index eabee0b253..fd68176a5a 100644
--- a/src/reducers/users.js
+++ b/src/reducers/users.js
@@ -8,6 +8,8 @@ export default function root(state = initialState, action) {
return Object.assign({}, state, {
currentUser: action.user,
});
+ case 'CLEAR_USER':
+ return (state = undefined);
default:
return state;
}
diff --git a/src/routes.js b/src/routes.js
index 2d7d3dfd24..a155577bef 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -1,9 +1,9 @@
// @flow
import React, { Component } from 'react';
//$FlowFixMe
-import { Router, Route, Switch, Redirect } from 'react-router';
+import { Route, Switch, Redirect } from 'react-router';
//$FlowFixMe
-import styled from 'styled-components';
+import styled, { ThemeProvider } from 'styled-components';
import generateMetaInfo from 'shared/generate-meta-info';
import { FlexCol } from './components/globals';
import { history } from './helpers/history';
@@ -26,6 +26,9 @@ import UserSettings from './views/userSettings';
import communitySettings from './views/communitySettings';
import channelSettings from './views/channelSettings';
import NewCommunity from './views/newCommunity';
+import Splash from './views/splash';
+import signedOutFallback from './helpers/signed-out-fallback';
+import { Login } from './views/login';
import ThreadSlider from './views/threadSlider';
const About = () =>
@@ -51,75 +54,96 @@ class Routes extends Component {
const { title, description } = generateMetaInfo();
return (
-
-
-
- {/* Default meta tags, get overriden by anything further down the tree */}
-
- {/* Global navigation, notifications, message notifications, etc */}
-
-
-
-
-
+
+
+ {/* Default meta tags, get overriden by anything further down the tree */}
+
+ {/* Global navigation, notifications, message notifications, etc */}
- {/*
- Switch only renders the first match. Subrouting happens downstream
- https://reacttraining.com/react-router/web/api/Switch
- */}
-
-
-
+
+
+
+
+
+
- {/* Public Business Pages */}
-
-
-
-
-
+ {/*
+ Switch only renders the first match. Subrouting happens downstream
+ https://reacttraining.com/react-router/web/api/Switch
+ */}
+
+
+
- {/* App Pages */}
-
- }
- />
-
-
-
-
-
- } />
-
-
-
+ {/* Public Business Pages */}
+
+
+
+
+
- {/*
- We check communitySlug last to ensure none of the above routes
- pass. We handle null communitySlug values downstream by either
- redirecting to home or showing a 404
- */}
-
-
-
-
-
-
-
-
+ {/* App Pages */}
+
+ }
+ />
+
+
+
+
+
+
+ } />
+
+
+
+
+ {/*
+ We check communitySlug last to ensure none of the above routes
+ pass. We handle null communitySlug values downstream by either
+ redirecting to home or showing a 404
+ */}
+
+
+
+
+
+
+
);
}
}
diff --git a/src/views/explore/index.js b/src/views/explore/index.js
index 38ef1ab1a9..d3a2779237 100644
--- a/src/views/explore/index.js
+++ b/src/views/explore/index.js
@@ -11,7 +11,7 @@ import Titlebar from '../titlebar';
import AppViewWrapper from '../../components/appViewWrapper';
import Head from '../../components/head';
import { Column } from '../../components/column';
-import { GoopyThree } from '../../views/homepage/style';
+import Goop from '../../components/goop';
import { FeaturedCommunity } from '../../components/curation';
import TopCommunityList from './components/topCommunities';
import Search from './components/search';
@@ -45,9 +45,8 @@ const ExplorePure = props => {
-
+
-
diff --git a/src/views/explore/style.js b/src/views/explore/style.js
index 48dbdbbffc..8656f3ad5a 100644
--- a/src/views/explore/style.js
+++ b/src/views/explore/style.js
@@ -104,7 +104,7 @@ export const SectionWrapper = styled(FlexRow)`
export const ViewHeader = styled(Section)`
flex: none;
- padding: 120px 0 160px 0;
+ padding: 120px 0 0 0;
justify-content: flex-end;
background-color: ${({ theme }) => theme.space.dark};
background-image: ${({ theme }) =>
@@ -114,7 +114,7 @@ export const ViewHeader = styled(Section)`
)}, ${theme.space.dark} )`};
@media (max-width: 768px) {
- padding: 48px 24px 96px 24px;
+ padding: 48px 24px 0 24px;
}
`;
@@ -265,6 +265,14 @@ export const SearchWrapper = styled(Card)`
position: relative;
margin-bottom: 48px;
padding: 12px 16px;
+ box-shadow: ${Shadow.low} ${props => hexa(props.theme.bg.reverse, 0.15)};
+ transition: ${Transition.hover.off};
+ z-index: ${zIndex.search};
+
+ &:hover{
+ box-shadow: ${Shadow.high} ${props => hexa(props.theme.bg.reverse, 0.25)};
+ transition: ${Transition.hover.on};
+ }
`;
export const SearchInputWrapper = styled(FlexRow)`
@@ -307,7 +315,6 @@ export const SearchResultsDropdown = styled.ul`
flex: auto;
max-height: 400px;
overflow-y: scroll;
- z-index: ${zIndex.dropDown};
background: ${props => props.theme.bg.default};
@media (max-width: 768px) {
@@ -369,11 +376,11 @@ export const SearchLink = styled(Link)`
`;
export const SearchResultImage = styled(Avatar)`
- margin: 8px 16px 8px 8px;
+ margin: 4px 6px 8px 4px;
`;
export const SearchResultMetaWrapper = styled(FlexCol)`
- margin-left: 4px;
+ margin-left: 16px;
`;
export const SearchResultName = styled.h2`
diff --git a/src/views/homepage/index.js b/src/views/homepage/index.js
deleted file mode 100644
index 376d5d22c8..0000000000
--- a/src/views/homepage/index.js
+++ /dev/null
@@ -1,262 +0,0 @@
-// @flow
-import React, { Component } from 'react';
-import { track } from '../../helpers/events';
-import Icon from '../../components/icons';
-import { FlexCol, FlexRow } from '../../components/globals';
-import { SERVER_URL } from '../../api';
-import { storeItem, getItemFromStorage } from '../../helpers/localStorage';
-import {
- SectionOne,
- SectionTwo,
- SectionThree,
- SectionFour,
- ClusterOne,
- ClusterTwo,
- ClusterThree,
- ClusterFour,
- GoopyOne,
- GoopyTwo,
- GoopyThree,
- GoopyFour,
- Wrapper,
- Tagline,
- ButtonTwitter,
- ButtonFacebook,
- ButtonGoogle,
- LogoContainer,
- LogoWhite,
- SectionContent,
- Copy,
- Footer,
- LinkBlock,
- LoginCard,
-} from './style';
-
-class Homepage extends Component {
- state: {
- preferredSigninMethod: string,
- };
-
- constructor() {
- super();
-
- const preferredSigninMethod = getItemFromStorage('preferred_signin_method');
- this.state = {
- preferredSigninMethod,
- };
- }
-
- componentDidMount() {
- track('homepage', 'viewed', null);
- }
-
- trackSignin = (type, method) => {
- track('homepage', 'logged in', type);
- storeItem('preferred_signin_method', method);
- };
-
- render() {
- const { preferredSigninMethod } = this.state;
-
- return (
-
-
-
-
-
-
-
- Where communities are built.
-
- this.trackSignin('primary cta', 'twitter')}
- >
- Sign in with Twitter
-
-
- this.trackSignin('primary cta', 'facebook')}
- >
- Sign in with Facebook
-
-
- this.trackSignin('primary cta', 'google')}
- >
- Sign in with Google
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- All your favorite communities. Only one you.
-
-
- For years people have been hacking different messaging platforms
- to support growing online communities.
-
-
- Spectrum was built from the ground up to keep you connected with
- the communities you care about in one simple feed.
-
-
-
-
-
-
-
-
- A better way to stay connected.
-
- In most apps, channels get jumbled, messages are lost, and that
- really great answer to your question from way-back-when is
- nowhere to be found.
-
-
- Spectrum keeps each conversation in its own unique and shareable
- place so that you can find it whenever you're ready.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Come on in, the chatter's fine.
- Spectrum is free for everyone, so dive on in!
-
- {preferredSigninMethod &&
-
-
- this.trackSignin('secondary cta', 'twitter')}
- >
- Sign in with Twitter
-
-
-
- this.trackSignin('secondary cta', 'facebook')}
- >
- {' '}
- Sign in with Facebook
-
-
-
- this.trackSignin('secondary cta', 'google')}
- >
- Sign in with Google
-
- }
-
- {!preferredSigninMethod &&
-
-
- this.trackSignin('secondary cta', 'twitter')}
- >
- Sign in with Twitter
-
-
-
- this.trackSignin('secondary cta', 'facebook')}
- >
- {' '}
- Sign in with Facebook
-
-
-
- this.trackSignin('secondary cta', 'google')}
- >
- Sign in with Google
-
- }
-
-
-
-
-
-
-
- );
- }
-}
-
-export default Homepage;
diff --git a/src/views/homepage/style.js b/src/views/homepage/style.js
deleted file mode 100644
index e0c984d4f8..0000000000
--- a/src/views/homepage/style.js
+++ /dev/null
@@ -1,412 +0,0 @@
-import styled from 'styled-components';
-import { Logo } from '../../components/logo';
-import {
- Gradient,
- H2,
- FlexCol,
- FlexRow,
- P,
- Transition,
- Shadow,
- zIndex,
-} from '../../components/globals';
-
-export const Wrapper = styled(FlexCol)`
- flex: 1 0 auto;
- width: 100%;
- background-color: ${({ theme }) => theme.bg.default};
- overflow: auto;
- overflow-x: hidden;
-`;
-
-export const Section = styled(FlexCol)`
- position: relative;
- flex: 1 0 720px;
- justify-content: center;
- padding-bottom: 80px;
-
- @media (max-width: 768px) {
- flex-basis: 480px;
- }
-
- img {
- width: 320px;
-
- @media (max-width: 768px) {
- display: none;
- }
- }
-`;
-
-export const SectionOne = styled(Section)`
- background-color: ${({ theme }) => theme.space.dark};
- background-image: ${({ theme }) =>
- Gradient(theme.space.dark, theme.brand.alt)};
- color: ${({ theme }) => theme.text.reverse};
-
- img {
- margin-left: 40px;
- width: 240px;
- }
-
- @media (max-width: 768px) {
- flex-basis: 600px;
- }
-`;
-
-export const SectionTwo = styled(Section)`
- justify-content: space-between;
- height: 640px;
- background-color: ${({ theme }) => theme.bg.default};
- color: ${({ theme }) => theme.text.default};
- padding-bottom: 160px;
-
- img {
- margin-right: 80px;
- width: 360px;
- }
-`;
-
-export const SectionThree = styled(Section)`
- height: 640px;
- justify-content: space-around;
- background-color: ${({ theme }) => theme.space.dark};
- background-image: linear-gradient(to bottom, ${({ theme }) =>
- `${theme.space.dark}, ${theme.brand.default}`});
- color: ${({ theme }) => theme.text.reverse};
-
- img {
- margin-left: 80px;
- }
-`;
-
-export const SectionFour = styled(Section)`
- height: 480px;
- justify-content: space-around;
- background-color: ${({ theme }) => theme.bg.default};
- color: ${({ theme }) => theme.text.default};
-
- img {
- margin-right: 80px;
- width: 420px;
- }
-
- button {
- margin-top: 24px;
- color: ${({ theme }) => theme.brand.default};
-
- &:hover {
- box-shadow: 0 4px 16px rgba(56, 24, 229, 0.5);
- }
- }
-`;
-
-export const Footer = styled(Section)`
- flex-direction: row;
- flex: 0 0 80px;
- background-color: ${({ theme }) => theme.space.light};
- color: ${({ theme }) => theme.text.reverse};
- justify-content: space-between;
- align-items: center;
- padding: 0 40px;
-
- @media (max-width: 768px) {
- flex-basis: auto;
- flex-direction: column;
- justify-content: flex-start;
- padding: 40px;
- }
-`;
-
-export const LinkBlock = styled.a`
- display: inline-block;
- margin: 0 24px;
- flex: 0 0 auto;
- position: relative;
-
- div {
- font-size: 16px;
- font-weight: 700;
- padding: 12px 16px;
- top: 4px;
- position: relative;
- text-align: center;
- transition: ${Transition.hover.off};
- border-radius: 4px;
-
- &:hover {
- border-radius: 12px;
- background-color: ${({ theme }) => theme.bg.default};
- color: #009eba;
- transition: ${Transition.hover.on};
- }
- }
-
- @media (max-width: 768px) {
- flex-direction: column;
- justify-content: flex-start;
- padding-bottom: 16px;
-
- div {
- border-bottom: none;
-
- &:hover {
- border-bottom: none;
- padding-bottom: 0;
- text-decoration: underline;
- }
- }
- }
-`;
-
-export const Cluster = styled.img`position: absolute;`;
-
-export const ClusterOne = styled(Cluster)`
- max-width: 120px;
- max-height: 120px;
- opacity: 0.15;
- top: 10%;
- left: 10%;
- z-index: ${zIndex.base};
-`;
-
-export const ClusterTwo = styled(Cluster)`
- max-width: 160px;
- max-height: 160px;
- opacity: 0.15;
- top: 60%;
- right: 10%;
- z-index: ${zIndex.base};
-`;
-
-export const ClusterThree = styled(Cluster)`
- max-width: 80px;
- max-height: 80px;
- opacity: 0.15;
- top: 10%;
- right: 40%;
- z-index: ${zIndex.base};
-`;
-
-export const ClusterFour = styled(Cluster)`
- max-width: 80px;
- max-height: 80px;
- opacity: 0.15;
- top: 80%;
- left: 40%;
- z-index: ${zIndex.base};
-`;
-
-export const GoopyOne = styled.div`
- background-color: transparent;
- background: url(/img/goopy.svg) center bottom no-repeat;
- position: absolute;
- background-size: 100%;
- z-index: ${zIndex.background};
- height: calc(100% + 4px);
- width: 110%;
- top: 0;
- bottom: -2px;
- left: -5%;
- right: -5%;
-`;
-
-export const GoopyTwo = styled.div`
- background-color: transparent;
- background: url(/img/goopy-2.svg) center bottom no-repeat;
- position: absolute;
- background-size: 100%;
- z-index: ${zIndex.background};
- height: calc(100% + 2px);
- top: 0;
- width: 110%;
- bottom: -2px;
- left: -5%;
- right: -5%;
-`;
-
-export const GoopyThree = styled.div`
- background-color: transparent;
- background: url(/img/goopy-3.svg) center bottom no-repeat;
- position: absolute;
- background-size: 100%;
- z-index: ${zIndex.background};
- height: calc(100% + 2px);
- top: 0;
- width: 110%;
- bottom: -2px;
- left: -5%;
- right: -5%;
-`;
-
-export const GoopyFour = styled.div`
- background-color: transparent;
- background: url(/img/goopy-4.svg) center bottom no-repeat;
- position: absolute;
- background-size: 100%;
- z-index: ${zIndex.background};
- height: calc(100% + 2px);
- top: 0;
- width: 110%;
- bottom: -2px;
- left: -5%;
- right: -5%;
-`;
-
-export const Tagline = styled(H2)`
- font-weight: 700;
- font-size: 24px;
- margin-top: 8px;
- margin-bottom: 24px;
- color: inherit;
-`;
-
-export const Button = styled.a`
- display: flex;
- flex-shrink: 1;
- z-index: ${zIndex.base + 1};
- flex-direction: flex-row;
- align-self: flex-start;
- align-items: center;
- color: ${({ theme }) => theme.text.reverse};
- border-radius: 8px;
- padding: 8px;
- padding-right: 16px;
- font-size: 14px;
- font-weight: 700;
- transition: ${Transition.hover.off};
- position: relative;
- margin: 16px 0;
-
- ${props =>
- props.after &&
- `
- margin: 24px 0;
-
- &:after {
- content: 'Previously signed in with';
- position: absolute;
- top: -23px;
- font-size: 10px;
- font-weight: 500;
- text-transform: uppercase;
- opacity: 0.8;
- left: 50%;
- transform: translateX(-50%);
- width: 100%;
- text-align: center;
- color: #fff;
- }
- `} span {
- display: inline-block;
- flex: 0 0 auto;
- margin-top: -1px;
- margin-left: 8px;
- line-height: 2.45;
- word-break: keep-all;
- white-space: nowrap;
- color: currentColor;
- }
-
- svg {
- fill: currentColor !important;
- }
-
- &:hover {
- cursor: pointer;
- }
-`;
-
-export const ButtonTwitter = styled(Button)`
- background: ${props =>
- props.preferred ? props.theme.social.twitter.default : 'none'};
- color: ${props =>
- props.whitebg
- ? props.theme.social.twitter.default
- : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
-
- &:hover {
- color: ${props =>
- props.whitebg ? props.theme.social.twitter.default : '#fff'}
- }
-`;
-
-export const ButtonFacebook = styled(Button)`
- background: ${props =>
- props.preferred ? props.theme.social.facebook.default : 'none'};
- color: ${props =>
- props.whitebg
- ? props.theme.social.facebook.default
- : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
-
-
- &:hover {
- color: ${props =>
- props.whitebg ? props.theme.social.facebook.default : '#fff'}
- }
-`;
-
-export const ButtonGoogle = styled(Button)`
- background: ${props =>
- props.preferred ? props.theme.social.google.default : 'none'};
- color: ${props =>
- props.whitebg
- ? props.theme.social.google.default
- : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
-
- &:hover {
- color: ${props =>
- props.whitebg ? props.theme.social.google.default : '#fff'}
- }
-`;
-
-export const LinkButton = styled(Button)`
- margin-top: 24px;
- color: ${({ theme }) => theme.brand.default};
- background: ${({ theme }) => theme.bg.default};
-
- &:hover {
- box-shadow: ${Shadow.high} ${({ theme }) => theme.space.soft};
- }
-`;
-
-export const LogoWhite = styled(Logo)`
- max-width: 360px;
-`;
-
-export const LogoContainer = styled.div`max-width: 360px;`;
-
-export const SectionContent = styled(FlexRow)`
- flex-grow: 1;
- width: 100%;
- align-items: center;
- justify-content: center;
- position: relative;
- z-index: ${zIndex.base + 1};
- padding: 0 10%;
-
- @media (max-width: 768px) {
- margin-top: 80px;
- }
-`;
-
-export const Copy = styled(P)`
- max-width: 400px;
- width: 100%;
- font-size: 18px;
- line-height: 24px;
- color: inherit;
- font-weight: 500;
-
- &:not(:first-of-type){
- margin-top: 24px;
- }
-`;
-
-export const LoginCard = styled.div`
- border-radius: 12px;
- padding: 16px 0;
- margin-top: 16px;
- align-self: flex-start;
- align-items: flex-start;
-`;
diff --git a/src/views/login/index.js b/src/views/login/index.js
new file mode 100644
index 0000000000..7ff178d12b
--- /dev/null
+++ b/src/views/login/index.js
@@ -0,0 +1,162 @@
+// @flow
+import React, { Component } from 'react';
+import Icon from '../../components/icons';
+import FullscreenView from '../../components/fullscreenView';
+import { getItemFromStorage, storeItem } from '../../helpers/localStorage';
+import { SERVER_URL, CLIENT_URL } from '../../api';
+import {
+ LargeTitle,
+ LargeSubtitle,
+ UpsellIconContainer,
+ FullscreenContent,
+ CodeOfConduct,
+ SigninButtonsContainer,
+ ButtonTwitter,
+ ButtonFacebook,
+ ButtonGoogle,
+ Col,
+} from './style';
+
+export class Login extends Component {
+ state: {
+ isSigningIn: Boolean,
+ signinType: string,
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isSigningIn: false,
+ signinType: props.signinType || 'signup',
+ };
+ }
+
+ toggleSigningIn = type => {
+ const { isSigningIn } = this.state;
+ this.setState({
+ isSigningIn: !isSigningIn,
+ signinType: type,
+ });
+ };
+
+ trackSignin = (type, method) => {
+ storeItem('preferred_signin_method', method);
+ };
+
+ render() {
+ const { signinType } = this.state;
+ const preferredSigninMethod = getItemFromStorage('preferred_signin_method');
+ const { redirectPath } = this.props;
+
+ const viewTitle =
+ signinType === 'login' ? 'Welcome back!' : 'Welcome to Spectrum!';
+
+ const viewSubtitle =
+ signinType === 'login'
+ ? "We're happy to see you again - log in below to get back into the conversation!"
+ : 'Spectrum is a place where communities can share, discuss, and grow together. Sign in below to get in on the conversation.';
+
+ const verb = signinType === 'login' ? 'Log in ' : 'Sign up ';
+
+ const postAuthRedirectPath = redirectPath
+ ? `?r=${redirectPath}`
+ : `?r=${CLIENT_URL}/home`;
+
+ return (
+
+
+
+
+
+
+ {viewTitle}
+
+
+ {viewSubtitle}
+
+
+
+ {preferredSigninMethod &&
+
+ this.trackSignin('secondary cta', 'twitter')}
+ >
+ {verb} with Twitter
+
+
+ this.trackSignin('secondary cta', 'facebook')}
+ >
+ {verb} with Facebook
+
+
+ this.trackSignin('secondary cta', 'google')}
+ >
+ {verb} with Google
+
+ }
+
+ {!preferredSigninMethod &&
+
+ this.trackSignin('secondary cta', 'twitter')}
+ >
+ {verb} with Twitter
+
+
+ this.trackSignin('secondary cta', 'facebook')}
+ >
+ {verb} with Facebook
+
+
+ this.trackSignin('secondary cta', 'google')}
+ >
+ {verb} with Google
+
+ }
+
+
+
+ By using Spectrum, you agree to our{' '}
+
+ Code of Conduct
+
+
+
+
+ );
+ }
+}
diff --git a/src/views/login/style.js b/src/views/login/style.js
new file mode 100644
index 0000000000..ada98e11a7
--- /dev/null
+++ b/src/views/login/style.js
@@ -0,0 +1,376 @@
+// @flow
+// $FlowFixMe
+import styled from 'styled-components';
+import {
+ FlexRow,
+ FlexCol,
+ Gradient,
+ Transition,
+ Shadow,
+ hexa,
+ zIndex,
+} from '../../components/globals';
+import { Button } from '../../components/buttons';
+
+export const Title = styled.h1`
+ color: ${props => props.theme.text.default};
+ width: 100%;
+ font-weight: 800;
+ font-size: 24px;
+ line-height: 1.25;
+ margin-bottom: 8px;
+ padding: 0;
+ font-size: 24px;
+ text-align: center;
+ letter-spacing: 0.2px;
+`;
+
+export const LargeTitle = styled(Title)`
+ font-size: 40px;
+ font-weight: 900;
+ letter-spacing: 0.3px;
+ margin-bottom: 16px;
+`;
+
+export const SmallTitle = styled(Title)`
+ font-size: 18px;
+`;
+
+export const MiniTitle = styled(Title)`
+ font-weight: 700;
+ font-size: 1rem;
+ line-height: 1.25;
+`;
+
+export const Actions = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ button {
+ margin: 0 8px;
+ }
+`;
+
+export const Subtitle = styled.h2`
+ width: 100%;
+ color: ${props => props.theme.text.alt};
+ font-weight: 500;
+ font-size: 16px;
+ line-height: 1.4;
+ margin-bottom: 16px;
+ padding: 0 32px;
+ text-align: center;
+
+ b {
+ font-weight: 700;
+ }
+
+ a {
+ color: ${props => props.theme.brand.default};
+ }
+
+ li {
+ margin-top: 8px;
+ list-style-type: none;
+ }
+`;
+
+export const LargeSubtitle = styled(Subtitle)`
+ font-size: 20px;
+`;
+
+export const MiniSubtitle = styled(Subtitle)`
+ font-weight: 600;
+ color: ${props => props.theme.text.alt};
+ font-size: 0.875rem;
+ line-height: 1.4;
+`;
+
+export const SmallSubtitle = styled(Subtitle)`
+ font-size: 15px;
+`;
+
+export const Cost = styled(Subtitle)`
+ margin-top: 8px;
+ font-weight: bold;
+`;
+
+export const NullCol = styled(FlexCol)`
+ background-image: ${props =>
+ props.bg ? `url('/img/fills/${props.bg}.svg')` : 'none'};
+ background-color: transparent;
+ background-size: 110% auto;
+ background-repeat: ${props => (props.repeat ? 'repeat-y' : 'no-repeat')};
+ background-position: ${props =>
+ props.repeat ? 'center top' : 'center center'};
+ width: 100%;
+ height: auto;
+ min-height: 160px;
+ flex: 0 0 auto;
+ padding: ${props => (props.noPadding ? '0' : '2rem')};
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ align-self: center;
+`;
+
+export const NullRow = styled(FlexRow)`
+ background-image: url('/img/fills/${props =>
+ props.bg ? `${props.bg}` : 'locked'}.svg');
+ background-color: transparent;
+ background-size: 110% auto;
+ background-repeat: no-repeat;
+ background-attachment: center;
+ width: 100%;
+ height: auto;
+ padding: 1rem 15%;
+`;
+
+export const UpgradeError = styled.p`
+ color: ${props => props.theme.warn.default};
+ font-size: 14px;
+ text-align: center;
+ margin: 16px 0 0;
+`;
+
+export const Profile = styled.div`
+ position: relative;
+ padding: 16px 0;
+
+ img {
+ border-radius: 48px;
+ width: 48px;
+ height: 48px;
+ }
+
+ span {
+ background-color: ${({ theme }) => theme.success.default};
+ background-image: ${({ theme }) =>
+ Gradient(theme.space.light, theme.success.default)};
+ position: absolute;
+ left: 75%;
+ top: 48px;
+ color: ${({ theme }) => theme.text.reverse};
+ font-size: 10px;
+ font-weight: 800;
+ padding: 2px 4px;
+ border-radius: 8px;
+ line-height: 1.5;
+ border: 2px solid #fff;
+ z-index: ${zIndex.avatar + 1};
+ }
+`;
+
+export const LargeEmoji = styled.div`
+ display: flex;
+ text-align: center;
+ flex 1;
+ padding: 16px 0 32px;
+ font-size: 48px;
+`;
+
+export const UpsellIconContainer = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 16px;
+ margin-top: 32px;
+ color: ${props => props.theme.text.alt};
+`;
+
+export const SignupButton = styled(Button)`
+ font-size: 18px;
+ font-weight: 700;
+ color: ${props => props.theme.text.reverse};
+ padding: 16px 88px;
+ max-width: 100%;
+ box-shadow: ${props =>
+ `${Shadow.high} ${hexa(props.theme.bg.reverse, 0.15)}`};
+ margin-top: 8px;
+`;
+
+export const SignupFooter = styled.div`
+ display: flex;
+ justify-content: center;
+ padding: 16px;
+ font-size: 14px;
+ color: ${props => props.theme.text.alt};
+ font-weight: 500;
+ border-top: 2px solid ${props => props.theme.bg.wash};
+ margin-top: 40px;
+ width: 100%;
+`;
+
+export const SigninLink = styled.span`
+ color: ${props => props.theme.brand.default};
+ margin-left: 6px;
+ cursor: pointer;
+`;
+
+export const FullscreenContent = styled.div`
+ width: 100%;
+ max-width: 768px;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ padding: 32px 16px;
+ flex: 1 0 auto;
+`;
+
+export const CodeOfConduct = styled.p`
+ display: inline-block;
+ font-size: 14px;
+ font-weight: 500;
+ color: ${props => props.theme.text.alt};
+ border-radius: 8px;
+ margin-top: 64px;
+ margin-left: 32px;
+ margin-right: 32px;
+ text-align: center;
+ position: relative;
+ z-index: ${zIndex.card + 1};
+
+ a {
+ color: ${props => props.theme.brand.default};
+ font-weight: 600;
+ }
+`;
+
+export const SigninButtonsContainer = styled.div`
+ display: flex;
+ padding-top: 48px;
+ max-width: 100%;
+
+ @media (max-width: 768px) {
+ padding-top: 24px;
+ }
+`;
+
+export const Col = styled.div`
+ display: flex;
+ align-items: flex-end;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ align-items: center;
+ }
+`;
+
+export const SigninButton = styled.a`
+ display: flex;
+ flex-shrink: 1;
+ z-index: ${zIndex.card + 1};
+ flex-direction: flex-row;
+ align-self: flex-start;
+ align-items: center;
+ color: ${({ theme }) => theme.text.reverse};
+ border-radius: 8px;
+ padding: 8px;
+ padding-right: 16px;
+ font-size: 14px;
+ font-weight: 700;
+ transition: ${Transition.hover.off};
+ position: relative;
+ margin: 16px;
+
+ ${props =>
+ props.after &&
+ `
+ &:after {
+ content: 'Previously signed in with';
+ position: absolute;
+ top: -32px;
+ font-size: 14px;
+ font-weight: 600;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 100%;
+ text-align: center;
+ color: ${props.theme.text.alt};
+ }
+ `} span {
+ display: inline-block;
+ flex: 0 0 auto;
+ margin-top: -1px;
+ margin-left: 8px;
+ line-height: 2.45;
+ word-break: keep-all;
+ white-space: nowrap;
+ color: currentColor;
+ }
+
+ svg {
+ fill: currentColor !important;
+ }
+
+ @media (max-width: 768px) {
+ margin: 16px 0;
+
+ ${props =>
+ props.after &&
+ `
+ margin: 48px 0 16px 0;
+ `};
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
+
+export const ButtonTwitter = styled(SigninButton)`
+ background: ${props =>
+ props.preferred ? props.theme.social.twitter.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.twitter.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+ &:after {
+ color: ${props => props.theme.social.twitter.default};
+ }
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.twitter.default : '#fff'}
+ }
+`;
+
+export const ButtonFacebook = styled(SigninButton)`
+ background: ${props =>
+ props.preferred ? props.theme.social.facebook.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.facebook.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+ &:after {
+ color: ${props => props.theme.social.facebook.default};
+ }
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.facebook.default : '#fff'}
+ }
+`;
+
+export const ButtonGoogle = styled(SigninButton)`
+ background: ${props =>
+ props.preferred ? props.theme.social.google.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.google.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+ &:after {
+ color: ${props => props.theme.social.google.default};
+ }
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.google.default : '#fff'}
+ }
+`;
diff --git a/src/views/login/view.js b/src/views/login/view.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/views/navbar/components/profileDropdown.js b/src/views/navbar/components/profileDropdown.js
index 4929a84dd8..dea159708d 100644
--- a/src/views/navbar/components/profileDropdown.js
+++ b/src/views/navbar/components/profileDropdown.js
@@ -50,9 +50,9 @@ export const ProfileDropdown = props => {
My Settings
}
-
+ {/*
Log Out
-
+ */}
);
diff --git a/src/views/navbar/index.js b/src/views/navbar/index.js
index ea711fb5da..681eaab964 100644
--- a/src/views/navbar/index.js
+++ b/src/views/navbar/index.js
@@ -297,6 +297,9 @@ class Navbar extends Component {
const isMobile = window.innerWidth < 768;
const currentUserExists =
loggedInUser !== null && loggedInUser !== undefined;
+ const isHome =
+ history.location.pathname === '/' ||
+ history.location.pathname === '/home';
const {
allUnseenCount,
dmUnseenCount,
@@ -304,6 +307,9 @@ class Navbar extends Component {
showNewUserOnboarding,
} = this.state;
+ // Bail out if the splash page is showing
+ if (!currentUserExists && isHome) return null;
+
// if the user is mobile and is viewing a thread or DM thread, don't
// render a navbar - it will be replaced with a chat input
const params = queryString.parse(history.location.search);
@@ -517,6 +523,5 @@ export default compose(
markNotificationsSeenMutation,
markNotificationsReadMutation,
markDirectMessageNotificationsSeenMutation,
- withRouter,
connect(mapStateToProps)
)(Navbar);
diff --git a/src/views/newCommunity/index.js b/src/views/newCommunity/index.js
index 87bca03386..6e0825cae6 100644
--- a/src/views/newCommunity/index.js
+++ b/src/views/newCommunity/index.js
@@ -20,6 +20,7 @@ import EditCommunityForm from './components/editCommunityForm';
import Titlebar from '../titlebar';
import Stepper from './components/stepper';
import Share from './components/share';
+import { Login } from '../../views/login';
import { getCommunityByIdQuery } from '../../api/community';
import {
Actions,
@@ -149,84 +150,96 @@ class NewCommunity extends Component {
};
render() {
+ const { currentUser } = this.props;
const { activeStep, community, existingId, hasInvitedPeople } = this.state;
const title = this.title();
const description = this.description();
- return (
-
-
-
-
-
-
- {title}
-
-
- {description}
-
-
- {// gather community meta info
- activeStep === 1 &&
- !community &&
- }
-
- {activeStep === 1 &&
- community &&
- }
-
- {activeStep === 2 &&
- community &&
- community.id &&
-
-
- ;
+ } else {
+ return (
+
+
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+ {// gather community meta info
+ activeStep === 1 &&
+ !community &&
+ }
+
+ {activeStep === 1 &&
+ community &&
+
-
-
- }
-
- {// connect a slack team or invite via email
- activeStep === 2 &&
-
- this.step('previous')}>
- Back
-
- {hasInvitedPeople
- ?
- : this.step('next')}
- >
- Skip this step
- }
- }
-
- {// share the community
- activeStep === 3 &&
-
-
- }
-
-
-
- );
+ />}
+
+ {activeStep === 2 &&
+ community &&
+ community.id &&
+
+
+
+
+
+ }
+
+ {// connect a slack team or invite via email
+ activeStep === 2 &&
+
+ this.step('previous')}>
+ Back
+
+ {hasInvitedPeople
+ ?
+ : this.step('next')}
+ >
+ Skip this step
+ }
+ }
+
+ {// share the community
+ activeStep === 3 &&
+
+
+ }
+
+
+
+ );
+ }
}
}
-
-export default compose(pure, withApollo, connect())(NewCommunity);
+const mapStateToProps = state => ({ currentUser: state.users.currentUser });
+export default compose(pure, withApollo, connect(mapStateToProps))(
+ NewCommunity
+);
diff --git a/src/views/newUserOnboarding/components/communitySearch/style.js b/src/views/newUserOnboarding/components/communitySearch/style.js
index 088d13426d..7780f5227c 100644
--- a/src/views/newUserOnboarding/components/communitySearch/style.js
+++ b/src/views/newUserOnboarding/components/communitySearch/style.js
@@ -6,7 +6,6 @@ import {
FlexRow,
Shadow,
hexa,
- Truncate,
zIndex,
} from '../../../../components/globals';
import { Avatar } from '../../../../components/avatar';
diff --git a/src/views/splash/components/illustrations.js b/src/views/splash/components/illustrations.js
new file mode 100644
index 0000000000..a8c8504045
--- /dev/null
+++ b/src/views/splash/components/illustrations.js
@@ -0,0 +1,110 @@
+// @flow
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import { zIndex, Shadow } from '../../../components/globals';
+
+export const Cluster = styled.img`
+ position: absolute;
+ opacity: 0.05;
+`;
+
+export const ClusterOne = styled(Cluster)`
+ max-width: 120px;
+ max-height: 120px;
+ top: 10%;
+ left: 10%;
+ z-index: ${zIndex.background};
+`;
+
+export const ClusterTwo = styled(Cluster)`
+ max-width: 160px;
+ max-height: 160px;
+ top: 60%;
+ right: 10%;
+ z-index: ${zIndex.background};
+`;
+
+export const ClusterThree = styled(Cluster)`
+ max-width: 80px;
+ max-height: 80px;
+ top: 10%;
+ right: 40%;
+ z-index: ${zIndex.background};
+`;
+
+export const ClusterFour = styled(Cluster)`
+ max-width: 80px;
+ max-height: 80px;
+ top: 80%;
+ left: 40%;
+ z-index: ${zIndex.background};
+`;
+
+export const Constellations = styled.div`
+ position: absolute;
+ background-color: transparent;
+ background: url('/img/constellations.svg') center top no-repeat;
+ background-size: cover 100%;
+ z-index: ${zIndex.background};
+ height: calc(100% + 4px);
+ width: 110%;
+ top: -10px;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ pointer-events: none;
+`;
+
+export const ConversationWrapper = styled.div`
+ position: relative;
+ z-index: ${zIndex.background};
+
+ max-width: 480px;
+ overflow-y: hidden;
+ box-shadow: 0 0 32px 24px ${props => props.theme.bg.default};
+ display: inline-block;
+ align-items: center;
+ justify-content: center;
+
+ @media (max-width: 768px) {
+ display: none;
+ }
+
+ > img {
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ pointer-events: none;
+ }
+
+ + div {
+ margin-left: 48px;
+
+ @media (max-width: 768px) {
+ margin-left: 0;
+ margin-bottom: 32px;
+ }
+ }
+`;
+
+export const Conversation = () =>
+
+
+ ;
+
+const DiscoverImage = styled.img`
+ position: relative;
+ left: -24px;
+ max-width: 400px;
+ height: auto;
+ object-fit: contain;
+
+ @media (max-width: 768px) {
+ left: auto;
+ margin-top: 32px;
+ max-width: 100%;
+ min-width: 256px;
+ }
+`;
+
+export const Discover = () => ;
diff --git a/src/views/splash/components/themes.js b/src/views/splash/components/themes.js
new file mode 100644
index 0000000000..0b2e8915ce
--- /dev/null
+++ b/src/views/splash/components/themes.js
@@ -0,0 +1,146 @@
+// @flow
+import React from 'react';
+// @Flow Fix Me
+import styled from 'styled-components';
+import Goop from '../../../components/goop';
+import {
+ ClusterOne,
+ ClusterTwo,
+ ClusterThree,
+ ClusterFour,
+ Constellations,
+} from './illustrations';
+import { FlexCol, hexa } from '../../../components/globals';
+
+export const Default = styled(FlexCol)`
+ display: flex;
+ position: relative;
+ flex: auto;
+ justify-content: center;
+ background-color: ${({ theme }) => theme.bg.default};
+ color: ${({ theme }) => theme.text.default};
+`;
+
+export const Primary = styled(Default)`
+ background-color: ${({ theme }) => theme.space.dark};
+ background-image: ${({ theme }) =>
+ `radial-gradient(farthest-corner at 50% 100%,
+ ${hexa(theme.brand.alt, 0.75)}, ${theme.space.dark}
+ )`};
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Brand = styled(Default)`
+ background-color: ${({ theme }) => theme.brand.default};
+ background-image: linear-gradient(to bottom, ${({ theme }) =>
+ `${theme.brand.alt}, ${theme.brand.default}`});
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Dark = styled(Default)`
+ background-color: ${({ theme }) => theme.space.dark};
+ background-image: linear-gradient(to bottom, ${({ theme }) =>
+ `${theme.space.dark}, ${theme.brand.default}`});
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Space = styled(Default)`
+background-color: ${({ theme }) => theme.space.dark};
+background-image: linear-gradient(to bottom, ${({ theme }) =>
+ `${theme.space.light}, ${theme.space.dark}`});
+color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Light = styled(Default)`
+ background-color: ${({ theme }) => theme.space.light};
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Bright = styled(Default)`
+ background-color: ${({ theme }) => theme.brand.default};
+ background-image: linear-gradient(to bottom, ${({ theme }) =>
+ `${theme.space.light}, ${theme.brand.default}`});
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+export const Grayscale = styled(Default)`
+ background-color: ${({ theme }) => theme.bg.reverse};
+ background-image: linear-gradient(to bottom, ${({ theme }) =>
+ `${theme.text.alt}, ${theme.bg.reverse}`});
+ color: ${({ theme }) => theme.text.reverse};
+`;
+
+const Theme = props => {
+ switch (props.background) {
+ default:
+ return (
+
+
+
+
+
+ {props.children}
+
+
+ );
+ case 'primary':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'brand':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'constellations':
+ return (
+
+ {props.children}
+
+
+
+ );
+ case 'dark':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'space':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'bright':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'light':
+ return (
+
+ {props.children}
+
+
+ );
+ case 'grayscale':
+ return (
+
+ {props.children}
+
+
+ );
+ }
+};
+
+export default Theme;
diff --git a/src/views/splash/index.js b/src/views/splash/index.js
new file mode 100644
index 0000000000..7b6ff031f1
--- /dev/null
+++ b/src/views/splash/index.js
@@ -0,0 +1,55 @@
+// @flow
+import React, { Component } from 'react';
+import { track } from '../../helpers/events';
+import { storeItem, getItemFromStorage } from '../../helpers/localStorage';
+import {
+ Overview,
+ Centralized,
+ CommunitySearch,
+ Chat,
+ Sell,
+ Yours,
+ PageFooter,
+} from './view';
+import { Wrapper } from './style';
+
+class Splash extends Component {
+ state: {
+ preferredSigninMethod: string,
+ };
+
+ constructor() {
+ super();
+
+ const preferredSigninMethod = getItemFromStorage('preferred_signin_method');
+ this.state = {
+ preferredSigninMethod,
+ };
+ }
+
+ componentDidMount() {
+ track('homepage', 'viewed', null);
+ }
+
+ trackSignin = (type, method) => {
+ track('homepage', 'logged in', type);
+ storeItem('preferred_signin_method', method);
+ };
+
+ render() {
+ const { preferredSigninMethod } = this.state;
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+export default Splash;
diff --git a/src/views/splash/style.js b/src/views/splash/style.js
new file mode 100644
index 0000000000..6899aa7451
--- /dev/null
+++ b/src/views/splash/style.js
@@ -0,0 +1,316 @@
+import styled from 'styled-components';
+import { Button } from '../../components/buttons';
+import {
+ H2,
+ FlexCol,
+ FlexRow,
+ P,
+ Transition,
+ Shadow,
+ zIndex,
+ hexa,
+} from '../../components/globals';
+
+export const Wrapper = styled(FlexCol)`
+ flex: 1 0 auto;
+ width: 100%;
+ background-color: ${({ theme }) => theme.bg.default};
+ overflow: auto;
+ overflow-x: hidden;
+ z-index: ${zIndex.base};
+`;
+
+export const Flexer = styled(FlexRow)`
+ flex-wrap: wrap;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ }
+`;
+
+export const Header = styled(FlexRow)`
+ padding: 32px;
+ justify-content: space-between;
+ z-index: ${zIndex.card}
+`;
+
+export const Content = styled(FlexRow)`
+ flex: auto;
+ align-self: stretch;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ }
+`;
+
+export const Tagline = styled(H2)`
+ font-weight: 900;
+ font-size: 32px;
+ margin-top: 8px;
+ margin-bottom: 8px;
+ color: inherit;
+
+ @media (max-width: 768px) {
+ margin-bottom: 32px;
+ }
+`;
+
+export const Copy = styled(P)`
+ max-width: 480px;
+ width: 100%;
+ font-size: 16px;
+ line-height: 1.5;
+ color: inherit;
+ font-weight: 500;
+
+ &:not(:first-of-type){
+ margin-top: 16px;
+ }
+
+ @media (max-width: 768px) {
+ text-align: left;
+ }
+`;
+
+export const Bullets = styled(FlexRow)`
+ align-self: stretch;
+ flex: auto;
+ justify-content: center;
+ align-items: flex-start;
+ margin: 32px 16px 16px;
+ flex-wrap: wrap;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ margin-top: 0;
+ }
+`;
+
+export const Bullet = styled(FlexCol)`
+ display: inline-block;
+ width: calc(33% - 64px);
+ min-width: 320px;
+ max-width: 480px;
+ margin: 32px;
+ margin-bottom: 0;
+
+ @media (max-width: 768px) {
+ width: 100%;
+ margin: 0;
+ margin-top: 48px;
+ }
+`;
+
+export const BulletHeading = styled(FlexRow)`
+ align-items: center;
+ white-space: nowrap;
+ position: relative;
+`;
+
+export const BulletTitle = styled.h2`
+ font-size: 20px;
+ font-weight: 700;
+`;
+
+export const BulletCopy = styled.p`
+ margin-top: 8px;
+ font-weight: 500;
+`;
+
+export const PrimaryCTA = styled(Button)`
+ padding: 8px 12px;
+ font-weight: 700;
+ font-size: 16px;
+ border-radius: 12px;
+ background-color: ${props => props.theme.bg.default};
+ background-image: none;
+ color: ${props => props.theme.brand.alt};
+ transition: ${Transition.hover.off};
+ z-index: ${zIndex.card};
+
+ &:hover {
+ background-color: ${props => props.theme.bg.default};
+ color: ${props => props.theme.brand.default};
+ box-shadow: ${Shadow.high} ${props => hexa(props.theme.bg.reverse, 0.5)};
+ transition: ${Transition.hover.on};
+ }
+`;
+
+export const SecondaryCTA = styled(PrimaryCTA)`
+ color: ${props => props.theme.text.reverse};
+ background-color: transparent;
+ border: 2px solid transparent;
+
+ &:hover {
+ color: ${props => props.theme.text.reverse};
+ background-color: transparent;
+ border-color: ${props => props.theme.bg.default};
+ box-shadow: 0 0 8px 4px ${props => hexa(props.theme.bg.default, 0.5)};
+ }
+`;
+
+export const SignInButton = styled.a`
+ display: flex;
+ flex-shrink: 1;
+ z-index: ${zIndex.base + 1};
+ flex-direction: flex-row;
+ align-self: flex-start;
+ align-items: center;
+ color: ${({ theme }) => theme.text.reverse};
+ border-radius: 8px;
+ padding: 8px;
+ padding-right: 16px;
+ font-size: 14px;
+ font-weight: 700;
+ transition: ${Transition.hover.off};
+ position: relative;
+ margin: 16px 0;
+
+ ${props =>
+ props.after &&
+ `
+ margin: 24px 0;
+
+ &:after {
+ content: 'Previously signed in with';
+ position: absolute;
+ top: -23px;
+ font-size: 10px;
+ font-weight: 500;
+ text-transform: uppercase;
+ opacity: 0.8;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 100%;
+ text-align: center;
+ color: #fff;
+ }
+ `} span {
+ display: inline-block;
+ flex: 0 0 auto;
+ margin-top: -1px;
+ margin-left: 8px;
+ line-height: 2.45;
+ word-break: keep-all;
+ white-space: nowrap;
+ color: currentColor;
+ }
+
+ svg {
+ fill: currentColor !important;
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
+
+export const LoginCard = styled.div`
+ border-radius: 12px;
+ padding: 16px 0;
+ margin-top: 16px;
+ align-self: flex-start;
+ align-items: flex-start;
+`;
+
+export const ButtonTwitter = styled(Button)`
+ background: ${props =>
+ props.preferred ? props.theme.social.twitter.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.twitter.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.twitter.default : '#fff'}
+ }
+`;
+
+export const ButtonFacebook = styled(Button)`
+ background: ${props =>
+ props.preferred ? props.theme.social.facebook.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.facebook.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.facebook.default : '#fff'}
+ }
+`;
+
+export const ButtonGoogle = styled(Button)`
+ background: ${props =>
+ props.preferred ? props.theme.social.google.default : 'none'};
+ color: ${props =>
+ props.whitebg
+ ? props.theme.social.google.default
+ : props.preferred ? '#fff' : 'rgba(255,255,255,0.8)'};
+
+ &:hover {
+ color: ${props =>
+ props.whitebg ? props.theme.social.google.default : '#fff'}
+ }
+`;
+
+export const Footer = styled(FlexRow)`
+ position: relative;
+ flex: auto;
+ justify-content: space-between;
+ padding: 24px 24px 24px 40px;
+ background-color: ${({ theme }) => theme.bg.reverse};
+ color: ${({ theme }) => theme.text.reverse};
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ padding: 20px;
+ }
+`;
+
+export const LinkBlock = styled.a`
+ display: inline-block;
+ margin: 0 24px;
+ flex: 0 0 auto;
+ position: relative;
+
+ &:hover {
+ text-decoration: none;
+ }
+
+ div {
+ font-size: 16px;
+ font-weight: 700;
+ padding: 12px 16px;
+ top: 4px;
+ position: relative;
+ text-align: center;
+ transition: ${Transition.hover.off};
+ border-radius: 12px;
+
+ &:hover {
+ background-color: ${({ theme }) => theme.bg.default};
+ color: ${({ theme }) => theme.text.default};
+ transition: ${Transition.hover.on};
+ }
+ }
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ justify-content: flex-start;
+ padding-bottom: 16px;
+
+ div {
+ border-bottom: none;
+
+ &:hover {
+ border-bottom: none;
+ }
+ }
+ }
+`;
diff --git a/src/views/splash/view.js b/src/views/splash/view.js
new file mode 100644
index 0000000000..2bdf6053f3
--- /dev/null
+++ b/src/views/splash/view.js
@@ -0,0 +1,521 @@
+// @flow
+import React from 'react';
+// @Flow Fix Me
+import styled from 'styled-components';
+// @Flow Fix Me
+import { Link } from 'react-router-dom';
+import { SERVER_URL } from '../../api';
+import { Button } from '../../components/buttons';
+import { Logo } from '../../components/logo';
+import Icon from '../../components/icons';
+import {
+ Shadow,
+ hexa,
+ Gradient,
+ FlexCol,
+ FlexRow,
+} from '../../components/globals';
+import Search from '../explore/components/search';
+
+import Theme from './components/themes';
+import { Conversation, Discover } from './components/illustrations';
+import {
+ Header,
+ Tagline,
+ Copy,
+ Bullets,
+ Bullet,
+ BulletHeading,
+ BulletTitle,
+ BulletCopy,
+ LinkBlock,
+ Footer,
+ Flexer,
+ PrimaryCTA,
+ SecondaryCTA,
+ Content,
+} from './style';
+
+// const Link = styled.a``;
+
+const Section = props =>
+
+ {props.children}
+ ;
+
+export const Overview = props => {
+ const Text = styled(FlexCol)`
+ margin: 60px 16px 120px 16px;
+ text-align: center;
+ align-items: center;
+
+ @media (max-width: 768px) {
+ margin-bottom: 16px;
+ }
+ `;
+
+ const ThisCopy = styled(Copy)`
+ font-size: 20px;
+ line-height: 1.3;
+ font-weight: 500;
+ opacity: 0.95;
+
+ @media (max-width: 768px) {
+ font-size: 20px;
+ text-align: center;
+ }
+ `;
+
+ const ThisTagline = styled(Tagline)`
+ margin-bottom: 16px;
+ font-size: 40px;
+ `;
+
+ const Actions = styled(Flexer)`
+ margin-top: 48px;
+ justify-content: space-between;
+ `;
+
+ const ThisSecondaryCTA = styled(SecondaryCTA)`
+ margin-left: 16px;
+ font-size: 16px;
+ border: 2px solid ${props => props.theme.text.reverse};
+
+ @media (max-width: 768px) {
+ margin-left: 0;
+ margin-top: 16px;
+ }
+ `;
+
+ const ThisPrimaryCTA = styled(PrimaryCTA)`
+ font-size: 16px;
+ `;
+
+ const ThisButton = styled(SecondaryCTA)`
+ padding: 12px 16px;
+ border: 2px solid ${props => props.theme.text.reverse};
+ `;
+
+ return (
+
+
+
+
+ Build better communities
+
+ Spectrum makes it easy to create and grow your online community.
+
+
+
+
+ Create a community
+
+
+
+
+ Find communities
+
+
+
+
+
+
+ );
+};
+
+export const Centralized = props => {
+ const ThisContent = styled(Content)`
+ img {
+ margin: 24px 0;
+ }
+ `;
+
+ const Text = styled(FlexCol)`
+ margin: 40px 16px 64px;
+
+ @media (max-width: 768px) {
+ margin-top: 20px;
+ margin-bottom: 44px;
+ }
+ `;
+
+ const ThisCopy = styled(Copy)`
+ margin-top: 16px;
+ `;
+
+ const ThisPrimaryCTA = styled(PrimaryCTA)`
+ margin-top: 32px;
+ background-color: ${props => props.theme.brand.alt};
+ background-image: ${props =>
+ Gradient(props.theme.brand.alt, props.theme.brand.default)};
+ color: ${props => props.theme.text.reverse};
+
+ &:hover {
+ color: ${props => props.theme.text.reverse};
+ }
+ `;
+
+ const Actions = styled.div`
+ @media (max-width: 768px) {
+ display: flex;
+ justify-content: center;
+ }
+ `;
+
+ const ThisTagline = styled(Tagline)`
+ @media (max-width: 768px) {
+ margin-bottom: 0;
+ }
+ `;
+
+ return (
+
+
+
+
+ Discoverable by default
+
+ People shouldn't have to hunt down an email invite or search through
+ a help center to find your community.
+
+
+ When it's built on Spectrum, people can find your community
+ organically with search, curation, and through other community
+ members.
+
+
+
+
+ Check out our top communities
+
+
+
+
+
+
+ );
+};
+
+export const CommunitySearch = props => {
+ const ThisContent = styled(Content)`
+ flex-direction: column;
+ width: 640px;
+ align-content: center;
+ align-self: center;
+ margin-top: 40px;
+ margin-bottom: 40px;
+ padding: 16px;
+
+ @media (max-width: 640px) {
+ margin-top: 80px;
+ margin-bottom: 0;
+ width: 100%;
+ }
+ `;
+
+ const ThisTagline = styled(Tagline)`
+ margin-bottom: 16px;
+ `;
+
+ const ThisCopy = styled(Copy)`
+ font-size: 18px;
+ margin-bottom: 32px;
+ font-weight: 500;
+ text-align: center;
+ max-width: 640px;
+
+ @media (max-width: 768px) {
+ text-align: left;
+ }
+ `;
+
+ return (
+
+
+ Find a community for you!
+
+ Try searching for topics like "crypto" or for products like "React"!
+
+
+
+
+ );
+};
+
+export const Chat = props => {
+ const ThisContent = styled(Content)`
+ overflow: hidden;
+ margin: 40px 16px;
+
+ @media (max-width: 768px) {
+ margin-bottom: 0;
+ }
+ `;
+
+ const ThisCopy = styled(Copy)`
+ margin-top: 16px;
+ `;
+
+ const ThisPrimaryCTA = styled(PrimaryCTA)`
+ background-color: ${props => props.theme.brand.alt};
+ background-image: ${props =>
+ Gradient(props.theme.brand.alt, props.theme.brand.default)};
+ color: ${props => props.theme.text.reverse};
+ margin-top: 32px;
+
+ &:hover {
+ color: ${props => props.theme.text.reverse};
+ }
+ `;
+
+ const Actions = styled.div`
+ @media (max-width: 768px) {
+ display: flex;
+ justify-content: center;
+ }
+ `;
+
+ const ThisTagline = styled(Tagline)`
+ @media (max-width: 768px) {
+ margin-bottom: 0;
+ }
+ `;
+
+ return (
+
+
+
+
+ Real-time messaging with long-term value
+
+ Conversations on Spectrum are real-time chat, just like your
+ favorite messaging app. But on Spectrum, conversations continue to
+ provide value to more and more people over time.
+
+
+ Every conversation gets a unique link to make it easy for people to
+ discover, share, or save for later.
+
+
+
+
+
+ View a live conversation
+
+
+
+
+
+
+ );
+};
+
+export const Sell = props => {
+ const Text = styled(FlexCol)`
+ align-items: center;
+ margin: 40px 0;
+ `;
+
+ const ThisContent = styled(Content)`
+
+ `;
+
+ const ThisTagline = styled(Tagline)`
+ margin-bottom: 0;
+ margin-left: 16px;
+ margin-right: 16px;
+ `;
+
+ const Actions = styled(Flexer)`
+ margin-bottom: 48px;
+ justify-content: center;
+ `;
+
+ const ThisSecondaryCTA = styled(SecondaryCTA)`
+ margin-left: 16px;
+
+ @media (max-width: 768px) {
+ margin-left: 0;
+ margin-top: 16px;
+ }
+ `;
+
+ const ThisSection = styled(Section)`
+ margin-bottom: 40px;
+ `;
+
+ return (
+
+
+
+ Spectrum saves you time and money
+
+
+
+ Supercharge support
+
+
+ Stop wasting time with endless private customer support threads
+ answering the same question over and over.
+
+
+ Now your team can have conversations with your community as a
+ whole and chat privately when a particular issue is sensitive.
+
+
+
+
+ Bring people together
+
+
+ Spectrum gives your top supporters and advocates a place to
+ share their knowledge, empower others, and foster a place of
+ belonging for everyone.
+
+
+
+
+ Tighten your feedback loop
+
+
+ There's no better feedback than the insights that come directly
+ from your customers.
+
+
+ Think of Spectrum as a new direct line to discovering what your
+ audience wants the most.
+
+
+
+
+
+
+
+
+ Start building your community
+
+
+
+
+ );
+};
+
+export const Yours = props => {
+ const ThisCopy = styled(Copy)`
+ max-width: 640px;
+ margin-top: 16px;
+ `;
+
+ const ThisContent = styled(Content)`
+ margin: 60px 16px 40px;
+ font-size: 18px;
+ align-items: center;
+ text-align: left;
+ `;
+
+ const ThisPrimaryCTA = styled(PrimaryCTA)`
+ background-color: ${props => props.theme.brand.alt};
+ background-image: ${props =>
+ Gradient(props.theme.brand.alt, props.theme.brand.default)};
+ color: ${props => props.theme.text.reverse};
+
+ &:hover {
+ color: ${props => props.theme.text.reverse};
+ }
+ `;
+
+ const ThisSecondaryCTA = styled(SecondaryCTA)`
+ margin-left: 16px;
+ background-color: transparent;
+ color: ${props => props.theme.brand.alt};
+ border-color: ${props => props.theme.brand.alt};
+
+ &:hover {
+ border-color: ${props => props.theme.brand.alt};
+ color: ${props => props.theme.brand.alt};
+ box-shadow: 0 0 8px ${props => hexa(props.theme.brand.alt, 1)};
+ }
+
+ > div {
+ top: -1px;
+ }
+
+ @media (max-width: 768px) {
+ margin-left: 0;
+ margin-top: 16px;
+ }
+ `;
+
+ const Actions = styled(Flexer)`
+ margin-top: 32px;
+ justify-content: flex-start;
+
+ > a {
+ display: inline-block;
+ }
+
+ @media (max-width: 768px) {
+ justify-content: center;
+ }
+ `;
+
+ return (
+
+
+
+ All your communities in one place
+
+ Before Spectrum, participating in online communities meant joining
+ multiple platforms, remembering different logins, and managing
+ endless notifications.
+
+
+ On Spectrum you only have one account, and the conversations that
+ matter come to you in one simple feed — no matter which
+ community they're in.
+
+
+
+
+ Create a community
+
+
+
+
+ Find communities
+
+
+
+
+
+
+ );
+};
+
+export const PageFooter = props => {
+ return (
+
+ );
+};
diff --git a/src/views/userSettings/components/emailSettings.js b/src/views/userSettings/components/emailSettings.js
index 8cd8dc0051..710f4ed504 100644
--- a/src/views/userSettings/components/emailSettings.js
+++ b/src/views/userSettings/components/emailSettings.js
@@ -67,7 +67,6 @@ class EmailSettings extends Component {
render() {
const { currentUser: { settings: { notifications } } } = this.props;
const settings = parseNotificationTypes(notifications);
- console.log('settings', settings);
return (