feat: Clicking the last updated time should open document history sidebar
Ref #1285
This commit is contained in:
@ -1,39 +1,123 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { inject } from "mobx-react";
|
import { inject, observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import ViewsStore from "stores/ViewsStore";
|
import AuthStore from "stores/AuthStore";
|
||||||
|
import CollectionsStore from "stores/CollectionsStore";
|
||||||
import Document from "models/Document";
|
import Document from "models/Document";
|
||||||
import PublishingInfo from "components/PublishingInfo";
|
import Breadcrumb from "components/Breadcrumb";
|
||||||
|
import Flex from "components/Flex";
|
||||||
|
import Time from "components/Time";
|
||||||
|
|
||||||
type Props = {|
|
const Container = styled(Flex)`
|
||||||
views: ViewsStore,
|
color: ${(props) => props.theme.textTertiary};
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Modified = styled.span`
|
||||||
|
color: ${(props) =>
|
||||||
|
props.highlight ? props.theme.text : props.theme.textTertiary};
|
||||||
|
font-weight: ${(props) => (props.highlight ? "600" : "400")};
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
collections: CollectionsStore,
|
||||||
|
auth: AuthStore,
|
||||||
|
showCollection?: boolean,
|
||||||
|
showPublished?: boolean,
|
||||||
document: Document,
|
document: Document,
|
||||||
isDraft: boolean,
|
children: React.Node,
|
||||||
|};
|
to?: string,
|
||||||
|
};
|
||||||
|
|
||||||
function DocumentMeta({ views, isDraft, document }: Props) {
|
function DocumentMeta({
|
||||||
const totalViews = views.countForDocument(document.id);
|
auth,
|
||||||
|
collections,
|
||||||
|
showPublished,
|
||||||
|
showCollection,
|
||||||
|
document,
|
||||||
|
children,
|
||||||
|
to,
|
||||||
|
...rest
|
||||||
|
}: Props) {
|
||||||
|
const {
|
||||||
|
modifiedSinceViewed,
|
||||||
|
updatedAt,
|
||||||
|
updatedBy,
|
||||||
|
createdAt,
|
||||||
|
publishedAt,
|
||||||
|
archivedAt,
|
||||||
|
deletedAt,
|
||||||
|
isDraft,
|
||||||
|
} = document;
|
||||||
|
|
||||||
|
// Prevent meta information from displaying if updatedBy is not available.
|
||||||
|
// Currently the situation where this is true is rendering share links.
|
||||||
|
if (!updatedBy) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content;
|
||||||
|
|
||||||
|
if (deletedAt) {
|
||||||
|
content = (
|
||||||
|
<span>
|
||||||
|
deleted <Time dateTime={deletedAt} /> ago
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (archivedAt) {
|
||||||
|
content = (
|
||||||
|
<span>
|
||||||
|
archived <Time dateTime={archivedAt} /> ago
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (createdAt === updatedAt) {
|
||||||
|
content = (
|
||||||
|
<span>
|
||||||
|
created <Time dateTime={updatedAt} /> ago
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (publishedAt && (publishedAt === updatedAt || showPublished)) {
|
||||||
|
content = (
|
||||||
|
<span>
|
||||||
|
published <Time dateTime={publishedAt} /> ago
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (isDraft) {
|
||||||
|
content = (
|
||||||
|
<span>
|
||||||
|
saved <Time dateTime={updatedAt} /> ago
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<Modified highlight={modifiedSinceViewed}>
|
||||||
|
updated <Time dateTime={updatedAt} /> ago
|
||||||
|
</Modified>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const collection = collections.get(document.collectionId);
|
||||||
|
const updatedByMe = auth.user && auth.user.id === updatedBy.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Meta document={document}>
|
<Container align="center" {...rest}>
|
||||||
{totalViews && !isDraft ? (
|
{updatedByMe ? "You" : updatedBy.name}
|
||||||
<>
|
{to ? <Link to={to}>{content}</Link> : content}
|
||||||
· Viewed{" "}
|
{showCollection && collection && (
|
||||||
{totalViews === 1 ? "once" : `${totalViews} times`}
|
<span>
|
||||||
</>
|
in
|
||||||
) : null}
|
<strong>
|
||||||
</Meta>
|
<Breadcrumb document={document} onlyText />
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Meta = styled(PublishingInfo)`
|
export default inject("collections", "auth")(observer(DocumentMeta));
|
||||||
margin: -12px 0 2em 0;
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default inject("views")(DocumentMeta);
|
|
||||||
|
48
app/components/DocumentMetaWithViews.js
Normal file
48
app/components/DocumentMetaWithViews.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// @flow
|
||||||
|
import { inject } from "mobx-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import ViewsStore from "stores/ViewsStore";
|
||||||
|
import Document from "models/Document";
|
||||||
|
import DocumentMeta from "components/DocumentMeta";
|
||||||
|
|
||||||
|
type Props = {|
|
||||||
|
views: ViewsStore,
|
||||||
|
document: Document,
|
||||||
|
isDraft: boolean,
|
||||||
|
to?: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
function DocumentMetaWithViews({ views, to, isDraft, document }: Props) {
|
||||||
|
const totalViews = views.countForDocument(document.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Meta document={document} to={to}>
|
||||||
|
{totalViews && !isDraft ? (
|
||||||
|
<>
|
||||||
|
· Viewed{" "}
|
||||||
|
{totalViews === 1 ? "once" : `${totalViews} times`}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</Meta>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Meta = styled(DocumentMeta)`
|
||||||
|
margin: -12px 0 2em 0;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default inject("views")(DocumentMetaWithViews);
|
@ -7,9 +7,9 @@ import styled, { withTheme } from "styled-components";
|
|||||||
import Document from "models/Document";
|
import Document from "models/Document";
|
||||||
import Badge from "components/Badge";
|
import Badge from "components/Badge";
|
||||||
import Button from "components/Button";
|
import Button from "components/Button";
|
||||||
|
import DocumentMeta from "components/DocumentMeta";
|
||||||
import Flex from "components/Flex";
|
import Flex from "components/Flex";
|
||||||
import Highlight from "components/Highlight";
|
import Highlight from "components/Highlight";
|
||||||
import PublishingInfo from "components/PublishingInfo";
|
|
||||||
import Tooltip from "components/Tooltip";
|
import Tooltip from "components/Tooltip";
|
||||||
import DocumentMenu from "menus/DocumentMenu";
|
import DocumentMenu from "menus/DocumentMenu";
|
||||||
import { newDocumentUrl } from "utils/routeHelpers";
|
import { newDocumentUrl } from "utils/routeHelpers";
|
||||||
@ -129,7 +129,7 @@ class DocumentPreview extends React.Component<Props> {
|
|||||||
processResult={this.replaceResultMarks}
|
processResult={this.replaceResultMarks}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<PublishingInfo
|
<DocumentMeta
|
||||||
document={document}
|
document={document}
|
||||||
showCollection={showCollection}
|
showCollection={showCollection}
|
||||||
showPublished={showPublished}
|
showPublished={showPublished}
|
||||||
|
@ -5,7 +5,7 @@ import { Link } from "react-router-dom";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { parseDocumentSlugFromUrl } from "shared/utils/parseDocumentIds";
|
import { parseDocumentSlugFromUrl } from "shared/utils/parseDocumentIds";
|
||||||
import DocumentsStore from "stores/DocumentsStore";
|
import DocumentsStore from "stores/DocumentsStore";
|
||||||
import DocumentMeta from "components/DocumentMeta";
|
import DocumentMetaWithViews from "components/DocumentMetaWithViews";
|
||||||
import Editor from "components/Editor";
|
import Editor from "components/Editor";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -27,7 +27,7 @@ function HoverPreviewDocument({ url, documents, children }: Props) {
|
|||||||
return children(
|
return children(
|
||||||
<Content to={document.url}>
|
<Content to={document.url}>
|
||||||
<Heading>{document.titleWithDefault}</Heading>
|
<Heading>{document.titleWithDefault}</Heading>
|
||||||
<DocumentMeta isDraft={document.isDraft} document={document} />
|
<DocumentMetaWithViews isDraft={document.isDraft} document={document} />
|
||||||
|
|
||||||
<Editor
|
<Editor
|
||||||
key={document.id}
|
key={document.id}
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
// @flow
|
|
||||||
import { inject, observer } from "mobx-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import styled from "styled-components";
|
|
||||||
import AuthStore from "stores/AuthStore";
|
|
||||||
import CollectionsStore from "stores/CollectionsStore";
|
|
||||||
import Document from "models/Document";
|
|
||||||
import Breadcrumb from "components/Breadcrumb";
|
|
||||||
import Flex from "components/Flex";
|
|
||||||
import Time from "components/Time";
|
|
||||||
|
|
||||||
const Container = styled(Flex)`
|
|
||||||
color: ${(props) => props.theme.textTertiary};
|
|
||||||
font-size: 13px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Modified = styled.span`
|
|
||||||
color: ${(props) =>
|
|
||||||
props.highlight ? props.theme.text : props.theme.textTertiary};
|
|
||||||
font-weight: ${(props) => (props.highlight ? "600" : "400")};
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
collections: CollectionsStore,
|
|
||||||
auth: AuthStore,
|
|
||||||
showCollection?: boolean,
|
|
||||||
showPublished?: boolean,
|
|
||||||
document: Document,
|
|
||||||
children: React.Node,
|
|
||||||
};
|
|
||||||
|
|
||||||
function PublishingInfo({
|
|
||||||
auth,
|
|
||||||
collections,
|
|
||||||
showPublished,
|
|
||||||
showCollection,
|
|
||||||
document,
|
|
||||||
children,
|
|
||||||
...rest
|
|
||||||
}: Props) {
|
|
||||||
const {
|
|
||||||
modifiedSinceViewed,
|
|
||||||
updatedAt,
|
|
||||||
updatedBy,
|
|
||||||
createdAt,
|
|
||||||
publishedAt,
|
|
||||||
archivedAt,
|
|
||||||
deletedAt,
|
|
||||||
isDraft,
|
|
||||||
} = document;
|
|
||||||
|
|
||||||
// Prevent meta information from displaying if updatedBy is not available.
|
|
||||||
// Currently the situation where this is true is rendering share links.
|
|
||||||
if (!updatedBy) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let content;
|
|
||||||
|
|
||||||
if (deletedAt) {
|
|
||||||
content = (
|
|
||||||
<span>
|
|
||||||
deleted <Time dateTime={deletedAt} /> ago
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (archivedAt) {
|
|
||||||
content = (
|
|
||||||
<span>
|
|
||||||
archived <Time dateTime={archivedAt} /> ago
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (createdAt === updatedAt) {
|
|
||||||
content = (
|
|
||||||
<span>
|
|
||||||
created <Time dateTime={updatedAt} /> ago
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (publishedAt && (publishedAt === updatedAt || showPublished)) {
|
|
||||||
content = (
|
|
||||||
<span>
|
|
||||||
published <Time dateTime={publishedAt} /> ago
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (isDraft) {
|
|
||||||
content = (
|
|
||||||
<span>
|
|
||||||
saved <Time dateTime={updatedAt} /> ago
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
content = (
|
|
||||||
<Modified highlight={modifiedSinceViewed}>
|
|
||||||
updated <Time dateTime={updatedAt} /> ago
|
|
||||||
</Modified>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const collection = collections.get(document.collectionId);
|
|
||||||
const updatedByMe = auth.user && auth.user.id === updatedBy.id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container align="center" {...rest}>
|
|
||||||
{updatedByMe ? "You" : updatedBy.name}
|
|
||||||
{content}
|
|
||||||
{showCollection && collection && (
|
|
||||||
<span>
|
|
||||||
in
|
|
||||||
<strong>
|
|
||||||
<Breadcrumb document={document} onlyText />
|
|
||||||
</strong>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default inject("collections", "auth")(observer(PublishingInfo));
|
|
@ -8,10 +8,11 @@ import styled from "styled-components";
|
|||||||
import parseTitle from "shared/utils/parseTitle";
|
import parseTitle from "shared/utils/parseTitle";
|
||||||
import Document from "models/Document";
|
import Document from "models/Document";
|
||||||
import ClickablePadding from "components/ClickablePadding";
|
import ClickablePadding from "components/ClickablePadding";
|
||||||
import DocumentMeta from "components/DocumentMeta";
|
import DocumentMetaWithViews from "components/DocumentMetaWithViews";
|
||||||
import Editor from "components/Editor";
|
import Editor from "components/Editor";
|
||||||
import Flex from "components/Flex";
|
import Flex from "components/Flex";
|
||||||
import HoverPreview from "components/HoverPreview";
|
import HoverPreview from "components/HoverPreview";
|
||||||
|
import { documentHistoryUrl } from "utils/routeHelpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onChangeTitle: (event: SyntheticInputEvent<>) => void,
|
onChangeTitle: (event: SyntheticInputEvent<>) => void,
|
||||||
@ -88,7 +89,11 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
autoFocus={!title}
|
autoFocus={!title}
|
||||||
maxLength={100}
|
maxLength={100}
|
||||||
/>
|
/>
|
||||||
<DocumentMeta isDraft={isDraft} document={document} />
|
<DocumentMetaWithViews
|
||||||
|
isDraft={isDraft}
|
||||||
|
document={document}
|
||||||
|
to={documentHistoryUrl(document)}
|
||||||
|
/>
|
||||||
<Editor
|
<Editor
|
||||||
ref={this.editor}
|
ref={this.editor}
|
||||||
autoFocus={title && !this.props.defaultValue}
|
autoFocus={title && !this.props.defaultValue}
|
||||||
|
@ -4,7 +4,7 @@ import * as React from "react";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Document from "models/Document";
|
import Document from "models/Document";
|
||||||
import PublishingInfo from "components/PublishingInfo";
|
import DocumentMeta from "components/DocumentMeta";
|
||||||
import type { NavigationNode } from "types";
|
import type { NavigationNode } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -60,7 +60,7 @@ class ReferenceListItem extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
<Title>{document.title}</Title>
|
<Title>{document.title}</Title>
|
||||||
{document.updatedBy && (
|
{document.updatedBy && (
|
||||||
<PublishingInfo document={document} showCollection={showCollection} />
|
<DocumentMeta document={document} showCollection={showCollection} />
|
||||||
)}
|
)}
|
||||||
</DocumentLink>
|
</DocumentLink>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user