Team switcher

This commit is contained in:
Tom Moor
2018-11-11 16:24:05 -08:00
parent 10e1f0231c
commit cc9f32cdc9
8 changed files with 107 additions and 26 deletions

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import styled, { withTheme } from 'styled-components'; import styled, { withTheme } from 'styled-components';
import { ExpandedIcon } from 'outline-icons'; import { ExpandedIcon } from 'outline-icons';
import Flex from 'shared/components/Flex'; import Flex from 'shared/components/Flex';
import TeamLogo from './TeamLogo'; import TeamLogo from 'shared/components/TeamLogo';
type Props = { type Props = {
teamName: string, teamName: string,

View File

@ -20,7 +20,7 @@ router.use('/', google.routes());
router.get('/redirect', auth(), async ctx => { router.get('/redirect', auth(), async ctx => {
const user = ctx.state.user; const user = ctx.state.user;
// transfer cookie from root to subdomain specific // transfer access token cookie from root to subdomain
ctx.cookies.set('accessToken', undefined, { ctx.cookies.set('accessToken', undefined, {
httpOnly: true, httpOnly: true,
domain: stripSubdomain(ctx.request.hostname), domain: stripSubdomain(ctx.request.hostname),

View File

@ -118,6 +118,7 @@ export default function auth(options?: { required?: boolean } = {}) {
[team.subdomain]: { [team.subdomain]: {
name: team.name, name: team.name,
logo: team.logo, logo: team.logo,
url: team.url,
expires, expires,
}, },
}); });

View File

@ -15,9 +15,10 @@ export const screenshotUrl = `${process.env.URL}/screenshot.png`;
type Props = { type Props = {
children?: React.Node, children?: React.Node,
sessions?: Object,
}; };
function Layout({ children }: Props) { function Layout({ children, sessions }: Props) {
return ( return (
<html lang="en"> <html lang="en">
<head> <head>
@ -65,7 +66,7 @@ function Layout({ children }: Props) {
{'{{CSS}}'} {'{{CSS}}'}
</head> </head>
<Body> <Body>
<TopNavigation /> <TopNavigation sessions={sessions} />
{children} {children}
<BottomNavigation /> <BottomNavigation />
</Body> </Body>

View File

@ -1,8 +1,10 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import { sortBy } from 'lodash';
import styled from 'styled-components'; import styled from 'styled-components';
import breakpoint from 'styled-components-breakpoint'; import breakpoint from 'styled-components-breakpoint';
import Centered from './Centered'; import Centered from './Centered';
import TeamLogo from '../../../shared/components/TeamLogo';
import { import {
developers, developers,
changelog, changelog,
@ -13,7 +15,18 @@ import {
spectrumUrl, spectrumUrl,
} from '../../../shared/utils/routeHelpers'; } from '../../../shared/utils/routeHelpers';
function TopNavigation() { type Sessions = {
[subdomain: string]: {
name: string,
logo: string,
expires: string,
url: string,
},
};
function TopNavigation({ sessions }: { sessions: ?Sessions }) {
const orderedSessions = sortBy(sessions, 'name');
return ( return (
<Nav> <Nav>
<Brand href={process.env.URL}>Outline</Brand> <Brand href={process.env.URL}>Outline</Brand>
@ -33,9 +46,25 @@ function TopNavigation() {
<MenuItem> <MenuItem>
<a href={developers()}>API</a> <a href={developers()}>API</a>
</MenuItem> </MenuItem>
<MenuItem> {orderedSessions.length ? (
<a href="/#signin">Sign In</a> <MenuItem highlighted>
</MenuItem> <a href={developers()}>Your Teams</a>
<ol>
{orderedSessions.map(session => (
<MenuItem key={session.url}>
<a href={`${session.url}/dashboard`}>
<TeamLogo src={session.logo} width={20} height={20} />
{session.name}
</a>
</MenuItem>
))}
</ol>
</MenuItem>
) : (
<MenuItem>
<a href="/#signin">Sign In</a>
</MenuItem>
)}
</Menu> </Menu>
</Nav> </Nav>
); );
@ -74,13 +103,8 @@ const MenuLinkStyle = props => `
} }
`; `;
const Menu = styled.ul`
margin: 0;
padding: 0;
list-style: none;
`;
const MenuItem = styled.li` const MenuItem = styled.li`
position: relative;
display: inline-block; display: inline-block;
margin: 0 0 0 40px; margin: 0 0 0 40px;
@ -89,6 +113,33 @@ const MenuItem = styled.li`
} }
${MenuLinkStyle}; ${MenuLinkStyle};
${props =>
props.highlighted &&
`
position: relative;
border: 2px solid ${props.theme.slate};
border-radius: 4px;
padding: 6px 8px;
margin-top: -6px;
margin-bottom: -6px;
&:hover {
border: 2px solid ${props.theme.slateDark};
> a {
color: ${props.theme.slateDark};
}
}
> a:hover {
text-decoration: none;
}
`};
&:hover ol {
display: block;
}
`; `;
const MenuItemDesktop = styled(MenuItem)` const MenuItemDesktop = styled(MenuItem)`
@ -99,6 +150,42 @@ const MenuItemDesktop = styled(MenuItem)`
`}; `};
`; `;
const Menu = styled.ul`
margin: 0;
padding: 0;
list-style: none;
ol {
display: none;
position: absolute;
margin: 0;
padding: 0;
right: 0;
top: 32px;
background: #fff;
border-radius: 4px;
min-width: 160px;
padding: 0 0.5em;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08),
0 2px 4px rgba(0, 0, 0, 0.08);
${MenuItem} {
padding: 0.5em 0;
margin: 0;
}
${MenuItem} a {
display: flex;
align-items: center;
}
${TeamLogo} {
margin-right: 0.5em;
}
}
`;
const Nav = styled(Centered)` const Nav = styled(Centered)`
display: flex; display: flex;
padding: 20px 0; padding: 20px 0;

View File

@ -66,21 +66,12 @@ router.get('/changelog', async ctx => {
// home page // home page
router.get('/', async ctx => { router.get('/', async ctx => {
const lastSignedIn = ctx.cookies.get('lastSignedIn'); const lastSignedIn = ctx.cookies.get('lastSignedIn');
const sessions = JSON.parse(ctx.cookies.get('sessions') || '{}');
const domain = parseDomain(ctx.request.hostname); const domain = parseDomain(ctx.request.hostname);
const subdomain = domain ? domain.subdomain : undefined; const subdomain = domain ? domain.subdomain : undefined;
console.log('domain', domain); const accessToken = ctx.cookies.get('accessToken');
console.log('subdomain', subdomain);
let accessToken;
if (subdomain) {
accessToken = sessions[subdomain] && sessions[subdomain].accessToken;
} else {
accessToken = sessions.root
? sessions.root.accessToken
: ctx.cookies.get('accessToken');
}
console.log('accessToken', accessToken); console.log('accessToken', accessToken);
ctx.set('Cache-Control', 'no-cache');
if (accessToken) { if (accessToken) {
return renderapp(ctx); return renderapp(ctx);

View File

@ -13,10 +13,11 @@ import theme from '../../shared/styles/theme';
const sheet = new ServerStyleSheet(); const sheet = new ServerStyleSheet();
export default function renderpage(ctx: Object, children: React.Node) { export default function renderpage(ctx: Object, children: React.Node) {
const sessions = JSON.parse(ctx.cookies.get('sessions') || '{}');
const html = ReactDOMServer.renderToString( const html = ReactDOMServer.renderToString(
<StyleSheetManager sheet={sheet.instance}> <StyleSheetManager sheet={sheet.instance}>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Layout>{children}</Layout> <Layout sessions={sessions}>{children}</Layout>
</ThemeProvider> </ThemeProvider>
</StyleSheetManager> </StyleSheetManager>
); );