fix: Show starred docs in sidebar (#2317)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@ -2,12 +2,11 @@
|
||||
import { observer } from "mobx-react";
|
||||
import {
|
||||
EditIcon,
|
||||
SearchIcon,
|
||||
ShapesIcon,
|
||||
HomeIcon,
|
||||
PlusIcon,
|
||||
SearchIcon,
|
||||
SettingsIcon,
|
||||
ShapesIcon,
|
||||
StarredIcon,
|
||||
} from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { DndProvider } from "react-dnd";
|
||||
@ -25,6 +24,7 @@ import ArchiveLink from "./components/ArchiveLink";
|
||||
import Collections from "./components/Collections";
|
||||
import Section from "./components/Section";
|
||||
import SidebarLink from "./components/SidebarLink";
|
||||
import Starred from "./components/Starred";
|
||||
import TeamButton from "./components/TeamButton";
|
||||
import TrashLink from "./components/TrashLink";
|
||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||
@ -109,12 +109,6 @@ function MainSidebar() {
|
||||
label={t("Search")}
|
||||
exact={false}
|
||||
/>
|
||||
<SidebarLink
|
||||
to="/starred"
|
||||
icon={<StarredIcon color="currentColor" />}
|
||||
exact={false}
|
||||
label={t("Starred")}
|
||||
/>
|
||||
{can.createDocument && (
|
||||
<SidebarLink
|
||||
to="/drafts"
|
||||
@ -135,6 +129,7 @@ function MainSidebar() {
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
<Starred />
|
||||
<Section auto>
|
||||
<Collections
|
||||
onCreateCollection={handleCreateCollectionModalOpen}
|
||||
|
@ -159,6 +159,7 @@ function CollectionLink({
|
||||
/>
|
||||
}
|
||||
exact={false}
|
||||
depth={0.5}
|
||||
menu={
|
||||
<>
|
||||
{can.update && (
|
||||
@ -198,7 +199,7 @@ function CollectionLink({
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
canUpdate={canUpdate}
|
||||
depth={1.5}
|
||||
depth={2}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
|
@ -1,16 +1,16 @@
|
||||
// @flow
|
||||
import fractionalIndex from "fractional-index";
|
||||
import { observer } from "mobx-react";
|
||||
import { PlusIcon } from "outline-icons";
|
||||
import { PlusIcon, CollapsedIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useDrop } from "react-dnd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Fade from "components/Fade";
|
||||
import Flex from "components/Flex";
|
||||
import useStores from "../../../hooks/useStores";
|
||||
import CollectionLink from "./CollectionLink";
|
||||
import DropCursor from "./DropCursor";
|
||||
import Header from "./Header";
|
||||
import PlaceholderCollections from "./PlaceholderCollections";
|
||||
import SidebarLink from "./SidebarLink";
|
||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||
@ -25,6 +25,7 @@ function Collections({ onCreateCollection }: Props) {
|
||||
const [fetchError, setFetchError] = React.useState();
|
||||
const { ui, policies, documents, collections } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
const isPreloaded: boolean = !!collections.orderedData.length;
|
||||
const { t } = useTranslation();
|
||||
const team = useCurrentTeam();
|
||||
@ -99,6 +100,7 @@ function Collections({ onCreateCollection }: Props) {
|
||||
icon={<PlusIcon color="currentColor" />}
|
||||
label={`${t("New collection")}…`}
|
||||
exact
|
||||
depth={0.5}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@ -107,7 +109,11 @@ function Collections({ onCreateCollection }: Props) {
|
||||
if (!collections.isLoaded || fetchError) {
|
||||
return (
|
||||
<Flex column>
|
||||
<Header>{t("Collections")}</Header>
|
||||
<SidebarLink
|
||||
label={t("Collections")}
|
||||
icon={<Disclosure expanded={expanded} color="currentColor" />}
|
||||
disabled
|
||||
/>
|
||||
<PlaceholderCollections />
|
||||
</Flex>
|
||||
);
|
||||
@ -115,10 +121,19 @@ function Collections({ onCreateCollection }: Props) {
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<Header>{t("Collections")}</Header>
|
||||
{isPreloaded ? content : <Fade>{content}</Fade>}
|
||||
<SidebarLink
|
||||
onClick={() => setExpanded((prev) => !prev)}
|
||||
label={t("Collections")}
|
||||
icon={<Disclosure expanded={expanded} color="currentColor" />}
|
||||
/>
|
||||
{expanded && (isPreloaded ? content : <Fade>{content}</Fade>)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
const Disclosure = styled(CollapsedIcon)`
|
||||
transition: transform 100ms ease, fill 50ms !important;
|
||||
${({ expanded }) => !expanded && "transform: rotate(-90deg);"};
|
||||
`;
|
||||
|
||||
export default observer(Collections);
|
||||
|
13
app/components/Sidebar/components/Disclosure.js
Normal file
13
app/components/Sidebar/components/Disclosure.js
Normal file
@ -0,0 +1,13 @@
|
||||
// @flow
|
||||
import { CollapsedIcon } from "outline-icons";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Disclosure = styled(CollapsedIcon)`
|
||||
transition: transform 100ms ease, fill 50ms !important;
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
|
||||
${({ expanded }) => !expanded && "transform: rotate(-90deg);"};
|
||||
`;
|
||||
|
||||
export default Disclosure;
|
@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
import { observer } from "mobx-react";
|
||||
import { CollapsedIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@ -8,6 +7,7 @@ import styled from "styled-components";
|
||||
import Collection from "models/Collection";
|
||||
import Document from "models/Document";
|
||||
import Fade from "components/Fade";
|
||||
import Disclosure from "./Disclosure";
|
||||
import DropCursor from "./DropCursor";
|
||||
import DropToImport from "./DropToImport";
|
||||
import EditableTitle from "./EditableTitle";
|
||||
@ -210,7 +210,7 @@ function DocumentLink(
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ position: "relative" }} onDragLeave={resetHoverExpanding}>
|
||||
<Relative onDragLeave={resetHoverExpanding}>
|
||||
<Draggable
|
||||
key={node.id}
|
||||
ref={drag}
|
||||
@ -244,6 +244,7 @@ function DocumentLink(
|
||||
depth={depth}
|
||||
exact={false}
|
||||
showActions={menuOpen}
|
||||
scrollIntoViewIfNeeded={!document?.isStarred}
|
||||
ref={ref}
|
||||
menu={
|
||||
document && !isMoving ? (
|
||||
@ -263,7 +264,7 @@ function DocumentLink(
|
||||
{manualSort && (
|
||||
<DropCursor isActiveDrop={isOverReorder} innerRef={dropToReorder} />
|
||||
)}
|
||||
</div>
|
||||
</Relative>
|
||||
{expanded && !isDragging && (
|
||||
<>
|
||||
{node.children.map((childNode, index) => (
|
||||
@ -285,17 +286,13 @@ function DocumentLink(
|
||||
);
|
||||
}
|
||||
|
||||
const Draggable = styled("div")`
|
||||
opacity: ${(props) => (props.$isDragging || props.$isMoving ? 0.5 : 1)};
|
||||
pointer-events: ${(props) => (props.$isMoving ? "none" : "all")};
|
||||
const Relative = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const Disclosure = styled(CollapsedIcon)`
|
||||
transition: transform 100ms ease, fill 50ms !important;
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
|
||||
${({ expanded }) => !expanded && "transform: rotate(-90deg);"};
|
||||
const Draggable = styled.div`
|
||||
opacity: ${(props) => (props.$isDragging || props.$isMoving ? 0.5 : 1)};
|
||||
pointer-events: ${(props) => (props.$isMoving ? "none" : "all")};
|
||||
`;
|
||||
|
||||
const ObservedDocumentLink = observer(React.forwardRef(DocumentLink));
|
||||
|
@ -27,7 +27,7 @@ const Cursor = styled("div")`
|
||||
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
${(props) => (props.from === "collections" ? "top: 15px;" : "bottom: -7px;")}
|
||||
${(props) => (props.from === "collections" ? "top: 25px;" : "bottom: -7px;")}
|
||||
background: transparent;
|
||||
|
||||
::after {
|
||||
|
@ -5,10 +5,11 @@ import Flex from "components/Flex";
|
||||
const Header = styled(Flex)`
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
text-transform: uppercase;
|
||||
color: ${(props) => props.theme.sidebarText};
|
||||
letter-spacing: 0.04em;
|
||||
margin: 4px 16px;
|
||||
margin: 4px 12px;
|
||||
`;
|
||||
|
||||
export default Header;
|
||||
|
@ -31,6 +31,7 @@ type Props = {|
|
||||
activeClassName?: String,
|
||||
activeStyle?: Object,
|
||||
className?: string,
|
||||
scrollIntoViewIfNeeded?: boolean,
|
||||
exact?: boolean,
|
||||
isActive?: any,
|
||||
location?: Location,
|
||||
@ -52,6 +53,7 @@ const NavLink = ({
|
||||
location: locationProp,
|
||||
strict,
|
||||
style: styleProp,
|
||||
scrollIntoViewIfNeeded,
|
||||
to,
|
||||
...rest
|
||||
}: Props) => {
|
||||
@ -83,13 +85,13 @@ const NavLink = ({
|
||||
const style = isActive ? { ...styleProp, ...activeStyle } : styleProp;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isActive && linkRef.current) {
|
||||
if (isActive && linkRef.current && scrollIntoViewIfNeeded !== false) {
|
||||
scrollIntoView(linkRef.current, {
|
||||
scrollMode: "if-needed",
|
||||
behavior: "instant",
|
||||
});
|
||||
}
|
||||
}, [linkRef, isActive]);
|
||||
}, [linkRef, scrollIntoViewIfNeeded, isActive]);
|
||||
|
||||
const props = {
|
||||
"aria-current": (isActive && ariaCurrent) || null,
|
||||
|
@ -15,6 +15,7 @@ function PlaceholderCollections() {
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 4px 16px;
|
||||
margin-left: 40px;
|
||||
width: 75%;
|
||||
`;
|
||||
|
||||
|
@ -5,7 +5,7 @@ import Flex from "components/Flex";
|
||||
const Section = styled(Flex)`
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
margin: 0 8px 20px;
|
||||
margin: 0 8px 12px;
|
||||
min-width: ${(props) => props.theme.sidebarMinWidth}px;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
@ -28,6 +28,7 @@ type Props = {
|
||||
theme: Theme,
|
||||
exact?: boolean,
|
||||
depth?: number,
|
||||
scrollIntoViewIfNeeded?: boolean,
|
||||
};
|
||||
|
||||
function SidebarLink(
|
||||
@ -49,12 +50,13 @@ function SidebarLink(
|
||||
history,
|
||||
match,
|
||||
className,
|
||||
scrollIntoViewIfNeeded,
|
||||
}: Props,
|
||||
ref
|
||||
) {
|
||||
const style = React.useMemo(() => {
|
||||
return {
|
||||
paddingLeft: `${(depth || 0) * 16 + 16}px`,
|
||||
paddingLeft: `${(depth || 0) * 16 + 12}px`,
|
||||
};
|
||||
}, [depth]);
|
||||
|
||||
@ -73,6 +75,7 @@ function SidebarLink(
|
||||
<>
|
||||
<Link
|
||||
$isActiveDrop={isActiveDrop}
|
||||
scrollIntoViewIfNeeded={scrollIntoViewIfNeeded}
|
||||
activeStyle={isActiveDrop ? activeDropStyle : activeStyle}
|
||||
style={active ? activeStyle : style}
|
||||
onClick={onClick}
|
||||
@ -131,6 +134,7 @@ const Link = styled(NavLink)`
|
||||
padding: 6px 16px;
|
||||
border-radius: 4px;
|
||||
transition: background 50ms, color 50ms;
|
||||
user-select: none;
|
||||
background: ${(props) =>
|
||||
props.$isActiveDrop ? props.theme.slateDark : "inherit"};
|
||||
color: ${(props) =>
|
||||
@ -156,13 +160,11 @@ const Link = styled(NavLink)`
|
||||
`}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover + ${Actions},
|
||||
&:active + ${Actions} {
|
||||
display: inline-flex;
|
||||
&:hover + ${Actions}, &:active + ${Actions} {
|
||||
display: inline-flex;
|
||||
|
||||
svg {
|
||||
opacity: 0.75;
|
||||
}
|
||||
svg {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
|
171
app/components/Sidebar/components/Starred.js
Normal file
171
app/components/Sidebar/components/Starred.js
Normal file
@ -0,0 +1,171 @@
|
||||
// @flow
|
||||
import { observer } from "mobx-react";
|
||||
import { CollapsedIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Flex from "components/Flex";
|
||||
import PlaceholderCollections from "./PlaceholderCollections";
|
||||
import Section from "./Section";
|
||||
import SidebarLink from "./SidebarLink";
|
||||
import StarredLink from "./StarredLink";
|
||||
import useStores from "hooks/useStores";
|
||||
import useToasts from "hooks/useToasts";
|
||||
|
||||
const STARRED_PAGINATION_LIMIT = 10;
|
||||
const STARRED = "STARRED";
|
||||
|
||||
function Starred() {
|
||||
const [isFetching, setIsFetching] = React.useState(false);
|
||||
const [fetchError, setFetchError] = React.useState();
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
const [show, setShow] = React.useState("Nothing");
|
||||
const [offset, setOffset] = React.useState(0);
|
||||
const [upperBound, setUpperBound] = React.useState(STARRED_PAGINATION_LIMIT);
|
||||
const { showToast } = useToasts();
|
||||
const { documents } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const { fetchStarred, starred } = documents;
|
||||
|
||||
const fetchResults = React.useCallback(async () => {
|
||||
try {
|
||||
setIsFetching(true);
|
||||
await fetchStarred({
|
||||
limit: STARRED_PAGINATION_LIMIT,
|
||||
offset,
|
||||
});
|
||||
} catch (error) {
|
||||
showToast(t("Starred documents could not be loaded"), {
|
||||
type: "error",
|
||||
});
|
||||
setFetchError(error);
|
||||
} finally {
|
||||
setIsFetching(false);
|
||||
}
|
||||
}, [fetchStarred, offset, showToast, t]);
|
||||
|
||||
useEffect(() => {
|
||||
let stateInLocal;
|
||||
|
||||
try {
|
||||
stateInLocal = localStorage.getItem(STARRED);
|
||||
} catch (_) {
|
||||
// no-op Safari private mode
|
||||
}
|
||||
|
||||
if (!stateInLocal) {
|
||||
localStorage.setItem(STARRED, expanded ? "true" : "false");
|
||||
} else {
|
||||
setExpanded(stateInLocal === "true");
|
||||
}
|
||||
}, [expanded]);
|
||||
|
||||
useEffect(() => {
|
||||
setOffset(starred.length);
|
||||
if (starred.length <= STARRED_PAGINATION_LIMIT) {
|
||||
setShow("Nothing");
|
||||
} else if (starred.length >= upperBound) {
|
||||
setShow("More");
|
||||
} else if (starred.length < upperBound) {
|
||||
setShow("Less");
|
||||
}
|
||||
}, [starred, upperBound]);
|
||||
|
||||
useEffect(() => {
|
||||
if (offset === 0) {
|
||||
fetchResults();
|
||||
}
|
||||
}, [fetchResults, offset]);
|
||||
|
||||
const handleShowMore = React.useCallback(
|
||||
async (ev) => {
|
||||
setUpperBound(
|
||||
(previousUpperBound) => previousUpperBound + STARRED_PAGINATION_LIMIT
|
||||
);
|
||||
await fetchResults();
|
||||
},
|
||||
[fetchResults]
|
||||
);
|
||||
|
||||
const handleShowLess = React.useCallback((ev) => {
|
||||
setUpperBound(STARRED_PAGINATION_LIMIT);
|
||||
setShow("More");
|
||||
}, []);
|
||||
|
||||
const handleExpandClick = React.useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
try {
|
||||
localStorage.setItem(STARRED, !expanded ? "true" : "false");
|
||||
} catch (_) {
|
||||
// no-op Safari private mode
|
||||
}
|
||||
setExpanded((prev) => !prev);
|
||||
},
|
||||
[expanded]
|
||||
);
|
||||
|
||||
const content = starred.slice(0, upperBound).map((document, index) => {
|
||||
return (
|
||||
<StarredLink
|
||||
key={document.id}
|
||||
documentId={document.id}
|
||||
collectionId={document.collectionId}
|
||||
to={document.url}
|
||||
title={document.title}
|
||||
url={document.url}
|
||||
depth={2}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
if (!starred.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Flex column>
|
||||
<SidebarLink
|
||||
onClick={handleExpandClick}
|
||||
label={t("Starred")}
|
||||
icon={<Disclosure expanded={expanded} color="currentColor" />}
|
||||
/>
|
||||
{expanded && (
|
||||
<>
|
||||
{content}
|
||||
{show === "More" && !isFetching && (
|
||||
<SidebarLink
|
||||
onClick={handleShowMore}
|
||||
label={`${t("Show more")}…`}
|
||||
depth={2}
|
||||
/>
|
||||
)}
|
||||
{show === "Less" && !isFetching && (
|
||||
<SidebarLink
|
||||
onClick={handleShowLess}
|
||||
label={`${t("Show less")}…`}
|
||||
depth={2}
|
||||
/>
|
||||
)}
|
||||
{(isFetching || fetchError) && (
|
||||
<Flex column>
|
||||
<PlaceholderCollections />
|
||||
</Flex>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
const Disclosure = styled(CollapsedIcon)`
|
||||
transition: transform 100ms ease, fill 50ms !important;
|
||||
${({ expanded }) => !expanded && "transform: rotate(-90deg);"};
|
||||
`;
|
||||
|
||||
export default observer(Starred);
|
102
app/components/Sidebar/components/StarredLink.js
Normal file
102
app/components/Sidebar/components/StarredLink.js
Normal file
@ -0,0 +1,102 @@
|
||||
// @flow
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import Fade from "components/Fade";
|
||||
import useStores from "../../../hooks/useStores";
|
||||
import Disclosure from "./Disclosure";
|
||||
import SidebarLink from "./SidebarLink";
|
||||
import useBoolean from "hooks/useBoolean";
|
||||
import DocumentMenu from "menus/DocumentMenu";
|
||||
|
||||
type Props = {|
|
||||
depth: number,
|
||||
title: string,
|
||||
to: string,
|
||||
documentId: string,
|
||||
collectionId: string,
|
||||
|};
|
||||
|
||||
function StarredLink({ depth, title, to, documentId, collectionId }: Props) {
|
||||
const { collections, documents } = useStores();
|
||||
const collection = collections.get(collectionId);
|
||||
const document = documents.get(documentId);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
|
||||
|
||||
const childDocuments = collection
|
||||
? collection.getDocumentChildren(documentId)
|
||||
: [];
|
||||
|
||||
const hasChildDocuments = childDocuments.length > 0;
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
if (!document) {
|
||||
await documents.fetch(documentId);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, [collection, collectionId, collections, document, documentId, documents]);
|
||||
|
||||
const handleDisclosureClick = React.useCallback((ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
setExpanded((prevExpanded) => !prevExpanded);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Relative>
|
||||
<SidebarLink
|
||||
depth={depth}
|
||||
to={to}
|
||||
label={
|
||||
<>
|
||||
{hasChildDocuments && (
|
||||
<Disclosure
|
||||
expanded={expanded}
|
||||
onClick={handleDisclosureClick}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</>
|
||||
}
|
||||
exact={false}
|
||||
showActions={menuOpen}
|
||||
menu={
|
||||
document ? (
|
||||
<Fade>
|
||||
<DocumentMenu
|
||||
document={document}
|
||||
onOpen={handleMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
/>
|
||||
</Fade>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Relative>
|
||||
{expanded &&
|
||||
childDocuments.map((childDocument) => (
|
||||
<ObserveredStarredLink
|
||||
key={childDocument.id}
|
||||
depth={depth + 1}
|
||||
title={childDocument.title}
|
||||
to={childDocument.url}
|
||||
documentId={childDocument.id}
|
||||
collectionId={collectionId}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const Relative = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const ObserveredStarredLink = observer(StarredLink);
|
||||
|
||||
export default ObserveredStarredLink;
|
@ -8,7 +8,6 @@ import Drafts from "scenes/Drafts";
|
||||
import Error404 from "scenes/Error404";
|
||||
import Home from "scenes/Home";
|
||||
import Search from "scenes/Search";
|
||||
import Starred from "scenes/Starred";
|
||||
import Templates from "scenes/Templates";
|
||||
import Trash from "scenes/Trash";
|
||||
|
||||
@ -51,13 +50,12 @@ export default function AuthenticatedRoutes() {
|
||||
<Redirect from="/dashboard" to="/home" />
|
||||
<Route path="/home/:tab" component={Home} />
|
||||
<Route path="/home" component={Home} />
|
||||
<Route exact path="/starred" component={Starred} />
|
||||
<Route exact path="/starred/:sort" component={Starred} />
|
||||
<Route exact path="/templates" component={Templates} />
|
||||
<Route exact path="/templates/:sort" component={Templates} />
|
||||
<Route exact path="/drafts" component={Drafts} />
|
||||
<Route exact path="/archive" component={Archive} />
|
||||
<Route exact path="/trash" component={Trash} />
|
||||
<Redirect exact from="/starred" to="/home" />
|
||||
<Redirect exact from="/collections/*" to="/collection/*" />
|
||||
<Route exact path="/collection/:id/new" component={DocumentNew} />
|
||||
<Route exact path="/collection/:id/:tab" component={Collection} />
|
||||
|
@ -1,64 +0,0 @@
|
||||
// @flow
|
||||
import { observer } from "mobx-react";
|
||||
import { StarredIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type Match } from "react-router-dom";
|
||||
import { Action } from "components/Actions";
|
||||
import Empty from "components/Empty";
|
||||
import Heading from "components/Heading";
|
||||
import InputSearchPage from "components/InputSearchPage";
|
||||
import PaginatedDocumentList from "components/PaginatedDocumentList";
|
||||
import Scene from "components/Scene";
|
||||
import Tab from "components/Tab";
|
||||
import Tabs from "components/Tabs";
|
||||
import useStores from "hooks/useStores";
|
||||
import NewDocumentMenu from "menus/NewDocumentMenu";
|
||||
|
||||
type Props = {
|
||||
match: Match,
|
||||
};
|
||||
|
||||
function Starred(props: Props) {
|
||||
const { documents } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const { fetchStarred, starred, starredAlphabetical } = documents;
|
||||
const { sort } = props.match.params;
|
||||
|
||||
return (
|
||||
<Scene
|
||||
icon={<StarredIcon color="currentColor" />}
|
||||
title={t("Starred")}
|
||||
actions={
|
||||
<>
|
||||
<Action>
|
||||
<InputSearchPage source="starred" label={t("Search documents")} />
|
||||
</Action>
|
||||
<Action>
|
||||
<NewDocumentMenu />
|
||||
</Action>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Heading>{t("Starred")}</Heading>
|
||||
<PaginatedDocumentList
|
||||
heading={
|
||||
<Tabs>
|
||||
<Tab to="/starred" exact>
|
||||
{t("Recently updated")}
|
||||
</Tab>
|
||||
<Tab to="/starred/alphabetical" exact>
|
||||
{t("Alphabetical")}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
}
|
||||
empty={<Empty>{t("You’ve not starred any documents yet.")}</Empty>}
|
||||
fetch={fetchStarred}
|
||||
documents={sort === "alphabetical" ? starredAlphabetical : starred}
|
||||
showCollection
|
||||
/>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(Starred);
|
@ -7,10 +7,6 @@ export function homeUrl(): string {
|
||||
return "/home";
|
||||
}
|
||||
|
||||
export function starredUrl(): string {
|
||||
return "/starred";
|
||||
}
|
||||
|
||||
export function newCollectionUrl(): string {
|
||||
return "/collection/new";
|
||||
}
|
||||
|
@ -141,9 +141,12 @@
|
||||
"Collections": "Collections",
|
||||
"Untitled": "Untitled",
|
||||
"Document not supported – try Markdown, Plain text, HTML, or Word": "Document not supported – try Markdown, Plain text, HTML, or Word",
|
||||
"Starred documents could not be loaded": "Starred documents could not be loaded",
|
||||
"Starred": "Starred",
|
||||
"Show more": "Show more",
|
||||
"Show less": "Show less",
|
||||
"Delete {{ documentName }}": "Delete {{ documentName }}",
|
||||
"Home": "Home",
|
||||
"Starred": "Starred",
|
||||
"Settings": "Settings",
|
||||
"Invite people": "Invite people",
|
||||
"Create a collection": "Create a collection",
|
||||
@ -175,6 +178,7 @@
|
||||
"System": "System",
|
||||
"Light": "Light",
|
||||
"Dark": "Dark",
|
||||
"Switch team": "Switch team",
|
||||
"Log out": "Log out",
|
||||
"Show path to document": "Show path to document",
|
||||
"Path to document": "Path to document",
|
||||
@ -551,7 +555,6 @@
|
||||
"Zapier": "Zapier",
|
||||
"Zapier is a platform that allows Outline to easily integrate with thousands of other business tools. Head over to Zapier to setup a \"Zap\" and start programmatically interacting with Outline.'": "Zapier is a platform that allows Outline to easily integrate with thousands of other business tools. Head over to Zapier to setup a \"Zap\" and start programmatically interacting with Outline.'",
|
||||
"Open Zapier": "Open Zapier",
|
||||
"You’ve not starred any documents yet.": "You’ve not starred any documents yet.",
|
||||
"There are no templates just yet.": "There are no templates just yet.",
|
||||
"You can create templates to help your team create consistent and accurate documentation.": "You can create templates to help your team create consistent and accurate documentation.",
|
||||
"Trash is empty at the moment.": "Trash is empty at the moment.",
|
||||
|
@ -198,7 +198,7 @@ export const dark = {
|
||||
placeholder: colors.slateDark,
|
||||
|
||||
sidebarBackground: colors.veryDarkBlue,
|
||||
sidebarItemBackground: colors.transparent,
|
||||
sidebarItemBackground: lighten(0.015, colors.almostBlack),
|
||||
sidebarText: colors.slate,
|
||||
backdrop: "rgba(255, 255, 255, 0.3)",
|
||||
shadow: "rgba(0, 0, 0, 0.6)",
|
||||
|
Reference in New Issue
Block a user