From 2f681b1ce8b46ba47521bf8ab42b842e8b711f4e Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 9 Aug 2018 23:14:51 -0700 Subject: [PATCH] WIP: Dashboard tabs --- .../components/PublishingInfo.js | 20 ++- app/components/Sidebar/Main.js | 2 +- .../Sidebar/components/SidebarLink.js | 4 +- app/components/Tab.js | 26 ++++ app/components/Tabs.js | 10 ++ app/index.js | 122 +----------------- app/routes.js | 78 +++++++++++ app/scenes/Dashboard.js | 47 ++++--- app/stores/DocumentsStore.js | 20 ++- 9 files changed, 182 insertions(+), 147 deletions(-) create mode 100644 app/components/Tab.js create mode 100644 app/components/Tabs.js create mode 100644 app/routes.js diff --git a/app/components/DocumentPreview/components/PublishingInfo.js b/app/components/DocumentPreview/components/PublishingInfo.js index 34ed1105..11f2254a 100644 --- a/app/components/DocumentPreview/components/PublishingInfo.js +++ b/app/components/DocumentPreview/components/PublishingInfo.js @@ -24,7 +24,13 @@ type Props = { }; function PublishingInfo({ collection, document }: Props) { - const { modifiedSinceViewed, updatedAt, updatedBy, publishedAt } = document; + const { + modifiedSinceViewed, + updatedAt, + updatedBy, + publishedAt, + isDraft, + } = document; return ( @@ -35,20 +41,20 @@ function PublishingInfo({ collection, document }: Props) { ) : ( {updatedBy.name} - {publishedAt ? ( - -  modified - ) : ( + {isDraft ? (  saved + ) : ( + +  modified )} )} {collection && ( -  in {collection.name} +  in {isDraft ? 'Drafts' : collection.name} )} diff --git a/app/components/Sidebar/Main.js b/app/components/Sidebar/Main.js index 1b126e0b..f56d29f7 100644 --- a/app/components/Sidebar/Main.js +++ b/app/components/Sidebar/Main.js @@ -56,7 +56,7 @@ class MainSidebar extends React.Component {
- }> + } exact={false}> Home }> diff --git a/app/components/Sidebar/components/SidebarLink.js b/app/components/Sidebar/components/SidebarLink.js index 0ca4c3c9..cd643e8d 100644 --- a/app/components/Sidebar/components/SidebarLink.js +++ b/app/components/Sidebar/components/SidebarLink.js @@ -52,6 +52,7 @@ type Props = { iconColor?: string, active?: boolean, theme: Object, + exact?: boolean, }; @observer @@ -100,6 +101,7 @@ class SidebarLink extends React.Component { menu, menuOpen, hideExpandToggle, + exact, } = this.props; const Component = to ? StyledNavLink : StyledDiv; const showExpandIcon = @@ -113,7 +115,7 @@ class SidebarLink extends React.Component { style={active ? this.activeStyle : undefined} onClick={onClick} to={to} - exact + exact={exact !== false} > {icon && {icon}} {showExpandIcon && ( diff --git a/app/components/Tab.js b/app/components/Tab.js new file mode 100644 index 00000000..fab0f010 --- /dev/null +++ b/app/components/Tab.js @@ -0,0 +1,26 @@ +// @flow +import * as React from 'react'; +import styled, { withTheme } from 'styled-components'; +import { NavLink } from 'react-router-dom'; + +const NavItem = styled(NavLink)` + display: inline-block; + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + color: ${props => props.theme.slate}; + letter-spacing: 0.04em; + margin-right: 20px; + padding-bottom: 8px; +`; + +function Tab(props: *) { + const activeStyle = { + paddingBottom: '5px', + borderBottom: `3px solid ${props.theme.slateLight}`, + }; + + return ; +} + +export default withTheme(Tab); diff --git a/app/components/Tabs.js b/app/components/Tabs.js new file mode 100644 index 00000000..78fd6312 --- /dev/null +++ b/app/components/Tabs.js @@ -0,0 +1,10 @@ +// @flow +import styled from 'styled-components'; + +const Tabs = styled.nav` + border-bottom: 1px solid ${props => props.theme.slateLight}; + margin-top: 22px; + margin-bottom: 10px; +`; + +export default Tabs; diff --git a/app/index.js b/app/index.js index 8a92ce64..3f9b7b27 100644 --- a/app/index.js +++ b/app/index.js @@ -3,54 +3,23 @@ import * as React from 'react'; import { render } from 'react-dom'; import { Provider } from 'mobx-react'; import { ThemeProvider } from 'styled-components'; -import { - BrowserRouter as Router, - Switch, - Route, - Redirect, -} from 'react-router-dom'; +import { BrowserRouter as Router } from 'react-router-dom'; import stores from 'stores'; import theme from 'shared/styles/theme'; import globalStyles from 'shared/styles/globals'; import 'shared/styles/prism.css'; -import Home from 'scenes/Home'; -import Dashboard from 'scenes/Dashboard'; -import Starred from 'scenes/Starred'; -import Drafts from 'scenes/Drafts'; -import Collection from 'scenes/Collection'; -import Document from 'scenes/Document'; -import Search from 'scenes/Search'; -import Settings from 'scenes/Settings'; -import Details from 'scenes/Settings/Details'; -import People from 'scenes/Settings/People'; -import Slack from 'scenes/Settings/Slack'; -import Shares from 'scenes/Settings/Shares'; -import Tokens from 'scenes/Settings/Tokens'; -import Export from 'scenes/Settings/Export'; -import Error404 from 'scenes/Error404'; - import ErrorBoundary from 'components/ErrorBoundary'; import ScrollToTop from 'components/ScrollToTop'; import ScrollToAnchor from 'components/ScrollToAnchor'; -import Layout from 'components/Layout'; -import Auth from 'components/Auth'; -import RouteSidebarHidden from 'components/RouteSidebarHidden'; - -import { matchDocumentSlug } from 'utils/routeHelpers'; +import Routes from './routes'; let DevTools; if (__DEV__) { DevTools = require('mobx-react-devtools').default; // eslint-disable-line global-require } -const notFoundSearch = () => ; -const DocumentNew = () => ; -const RedirectDocument = ({ match }: { match: Object }) => ( - -); - globalStyles(); const element = document.getElementById('root'); @@ -64,92 +33,7 @@ if (element) { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/app/routes.js b/app/routes.js new file mode 100644 index 00000000..b2be2b83 --- /dev/null +++ b/app/routes.js @@ -0,0 +1,78 @@ +// @flow +import * as React from 'react'; +import { Switch, Route, Redirect } from 'react-router-dom'; + +import Home from 'scenes/Home'; +import Dashboard from 'scenes/Dashboard'; +import Starred from 'scenes/Starred'; +import Drafts from 'scenes/Drafts'; +import Collection from 'scenes/Collection'; +import Document from 'scenes/Document'; +import Search from 'scenes/Search'; +import Settings from 'scenes/Settings'; +import Details from 'scenes/Settings/Details'; +import People from 'scenes/Settings/People'; +import Slack from 'scenes/Settings/Slack'; +import Shares from 'scenes/Settings/Shares'; +import Tokens from 'scenes/Settings/Tokens'; +import Export from 'scenes/Settings/Export'; +import Error404 from 'scenes/Error404'; + +import Layout from 'components/Layout'; +import Auth from 'components/Auth'; +import RouteSidebarHidden from 'components/RouteSidebarHidden'; +import { matchDocumentSlug as slug } from 'utils/routeHelpers'; + +const NotFound = () => ; +const DocumentNew = () => ; +const RedirectDocument = ({ match }: { match: Object }) => ( + +); + +export default function Routes() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/scenes/Dashboard.js b/app/scenes/Dashboard.js index 5b0a5ea0..d5ca3ddf 100644 --- a/app/scenes/Dashboard.js +++ b/app/scenes/Dashboard.js @@ -1,20 +1,25 @@ // @flow import * as React from 'react'; +import { Switch, Route } from 'react-router-dom'; import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { NewDocumentIcon } from 'outline-icons'; import DocumentsStore from 'stores/DocumentsStore'; +import AuthStore from 'stores/AuthStore'; import NewDocumentMenu from 'menus/NewDocumentMenu'; import Actions, { Action } from 'components/Actions'; import CenteredContent from 'components/CenteredContent'; import DocumentList from 'components/DocumentList'; import PageTitle from 'components/PageTitle'; import Subheading from 'components/Subheading'; +import Tabs from 'components/Tabs'; +import Tab from 'components/Tab'; import { ListPlaceholder } from 'components/LoadingPlaceholder'; type Props = { documents: DocumentsStore, + auth: AuthStore, }; @observer @@ -27,16 +32,20 @@ class Dashboard extends React.Component { loadContent = async () => { await Promise.all([ - this.props.documents.fetchRecentlyModified({ limit: 5 }), - this.props.documents.fetchRecentlyViewed({ limit: 5 }), + this.props.documents.fetchRecentlyModified({ limit: 15 }), + this.props.documents.fetchRecentlyViewed({ limit: 15 }), ]); this.isLoaded = true; }; render() { - const { documents } = this.props; + const { documents, auth } = this.props; + if (!auth.user) return; + const hasRecentlyViewed = documents.recentlyViewed.length > 0; const hasRecentlyEdited = documents.recentlyEdited.length > 0; + + const owned = documents.owned(auth.user.id); const showContent = this.isLoaded || (hasRecentlyViewed && hasRecentlyEdited); @@ -44,28 +53,34 @@ class Dashboard extends React.Component {

Home

+ + + Recently edited + + + Recently viewed + + Created by me + {showContent ? ( - {hasRecentlyViewed && ( - - Recently viewed + + - - )} - {hasRecentlyEdited && ( - - Recently edited + + + + + - - )} + + } /> @@ -80,4 +95,4 @@ class Dashboard extends React.Component { } } -export default inject('documents')(Dashboard); +export default inject('documents', 'auth')(Dashboard); diff --git a/app/stores/DocumentsStore.js b/app/stores/DocumentsStore.js index dbe746f5..dea90d0e 100644 --- a/app/stores/DocumentsStore.js +++ b/app/stores/DocumentsStore.js @@ -29,10 +29,8 @@ class DocumentsStore extends BaseStore { ui: UiStore; - /* Computed */ - @computed - get recentlyViewed(): Array { + get recentlyViewed(): Document[] { const docs = []; this.recentlyViewedIds.forEach(id => { const doc = this.getById(id); @@ -51,6 +49,17 @@ class DocumentsStore extends BaseStore { return docs; } + owned(userId: string): Document[] { + return _.orderBy( + _.filter( + this.data.values(), + document => document.createdBy.id === userId + ), + 'updatedAt', + 'desc' + ); + } + pinnedInCollection(collectionId: string): Document[] { return _.filter( this.recentlyEditedInCollection(collectionId), @@ -152,6 +161,11 @@ class DocumentsStore extends BaseStore { await this.fetchPage('pinned', options); }; + @action + fetchOwned = async (options: ?PaginationParams): Promise<*> => { + await this.fetchPage('owned', options); + }; + @action search = async ( query: string,