fix: Various editor header and metadata fixes (#2361)
* fix: Publish button disabled on drafts in read-only mode fix: Template selector appears on edited documents * fix: Save button does not immediately come available when selecting a template * fix: Template menu item alignment closes #2204 * fixes: Use policy for display of star in document title closes #2354 * fix: Modified time is sometimes bold when last edited user is current user closes #2355 * fix: Allow starring of drafts
This commit is contained in:
@ -8,6 +8,7 @@ import Document from "models/Document";
|
|||||||
import DocumentBreadcrumb from "components/DocumentBreadcrumb";
|
import DocumentBreadcrumb from "components/DocumentBreadcrumb";
|
||||||
import Flex from "components/Flex";
|
import Flex from "components/Flex";
|
||||||
import Time from "components/Time";
|
import Time from "components/Time";
|
||||||
|
import useCurrentUser from "hooks/useCurrentUser";
|
||||||
import useStores from "hooks/useStores";
|
import useStores from "hooks/useStores";
|
||||||
|
|
||||||
const Container = styled(Flex)`
|
const Container = styled(Flex)`
|
||||||
@ -50,7 +51,9 @@ function DocumentMeta({
|
|||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { collections, auth } = useStores();
|
const { collections } = useStores();
|
||||||
|
const user = useCurrentUser();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
modifiedSinceViewed,
|
modifiedSinceViewed,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
@ -69,6 +72,8 @@ function DocumentMeta({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const collection = collections.get(document.collectionId);
|
||||||
|
const lastUpdatedByCurrentUser = user.id === updatedBy.id;
|
||||||
let content;
|
let content;
|
||||||
|
|
||||||
if (deletedAt) {
|
if (deletedAt) {
|
||||||
@ -103,15 +108,12 @@ function DocumentMeta({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<Modified highlight={modifiedSinceViewed}>
|
<Modified highlight={modifiedSinceViewed && !lastUpdatedByCurrentUser}>
|
||||||
{t("updated")} <Time dateTime={updatedAt} addSuffix />
|
{t("updated")} <Time dateTime={updatedAt} addSuffix />
|
||||||
</Modified>
|
</Modified>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const collection = collections.get(document.collectionId);
|
|
||||||
const updatedByMe = auth.user && auth.user.id === updatedBy.id;
|
|
||||||
|
|
||||||
const timeSinceNow = () => {
|
const timeSinceNow = () => {
|
||||||
if (isDraft || !showLastViewed) {
|
if (isDraft || !showLastViewed) {
|
||||||
return null;
|
return null;
|
||||||
@ -137,7 +139,7 @@ function DocumentMeta({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container align="center" rtl={document.dir === "rtl"} {...rest} dir="ltr">
|
<Container align="center" rtl={document.dir === "rtl"} {...rest} dir="ltr">
|
||||||
{updatedByMe ? t("You") : updatedBy.name}
|
{lastUpdatedByCurrentUser ? t("You") : updatedBy.name}
|
||||||
{to ? <Link to={to}>{content}</Link> : content}
|
{to ? <Link to={to}>{content}</Link> : content}
|
||||||
{showCollection && collection && (
|
{showCollection && collection && (
|
||||||
<span>
|
<span>
|
||||||
|
@ -40,13 +40,13 @@ function TemplatesMenu({ document }: Props) {
|
|||||||
{...menu}
|
{...menu}
|
||||||
>
|
>
|
||||||
<DocumentIcon />
|
<DocumentIcon />
|
||||||
<div>
|
<TemplateItem>
|
||||||
<strong>{template.titleWithDefault}</strong>
|
<strong>{template.titleWithDefault}</strong>
|
||||||
<br />
|
<br />
|
||||||
<Author>
|
<Author>
|
||||||
{t("By {{ author }}", { author: template.createdBy.name })}
|
{t("By {{ author }}", { author: template.createdBy.name })}
|
||||||
</Author>
|
</Author>
|
||||||
</div>
|
</TemplateItem>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -70,9 +70,12 @@ function TemplatesMenu({ document }: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Author = styled.div`
|
const TemplateItem = styled.div`
|
||||||
font-size: 13px;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Author = styled.div`
|
||||||
|
font-size: 13px;
|
||||||
|
`;
|
||||||
|
|
||||||
export default observer(TemplatesMenu);
|
export default observer(TemplatesMenu);
|
||||||
|
@ -76,6 +76,10 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
@observable title: string = this.props.document.title;
|
@observable title: string = this.props.document.title;
|
||||||
getEditorText: () => string = () => this.props.document.text;
|
getEditorText: () => string = () => this.props.document.text;
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.updateIsDirty();
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { auth, document, t } = this.props;
|
const { auth, document, t } = this.props;
|
||||||
|
|
||||||
@ -113,6 +117,7 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
document.injectTemplate = false;
|
document.injectTemplate = false;
|
||||||
this.title = document.title;
|
this.title = document.title;
|
||||||
this.isDirty = true;
|
this.isDirty = true;
|
||||||
|
this.updateIsDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,6 +534,6 @@ const MaxWidth = styled(Flex)`
|
|||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
withTranslation()<DocumentScene>(
|
withTranslation()<DocumentScene>(
|
||||||
inject("ui", "auth", "policies", "revisions", "toasts")(DocumentScene)
|
inject("ui", "auth", "toasts")(DocumentScene)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { observer } from "mobx-react";
|
import { inject, observer } from "mobx-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Textarea from "react-autosize-textarea";
|
import Textarea from "react-autosize-textarea";
|
||||||
import { type TFunction, withTranslation } from "react-i18next";
|
import { type TFunction, withTranslation } from "react-i18next";
|
||||||
@ -9,6 +9,7 @@ import breakpoint from "styled-components-breakpoint";
|
|||||||
import { MAX_TITLE_LENGTH } from "shared/constants";
|
import { MAX_TITLE_LENGTH } from "shared/constants";
|
||||||
import { light } from "shared/theme";
|
import { light } from "shared/theme";
|
||||||
import parseTitle from "shared/utils/parseTitle";
|
import parseTitle from "shared/utils/parseTitle";
|
||||||
|
import PoliciesStore from "stores/PoliciesStore";
|
||||||
import Document from "models/Document";
|
import Document from "models/Document";
|
||||||
import ClickablePadding from "components/ClickablePadding";
|
import ClickablePadding from "components/ClickablePadding";
|
||||||
import DocumentMetaWithViews from "components/DocumentMetaWithViews";
|
import DocumentMetaWithViews from "components/DocumentMetaWithViews";
|
||||||
@ -29,6 +30,7 @@ type Props = {|
|
|||||||
onSave: ({ done?: boolean, autosave?: boolean, publish?: boolean }) => any,
|
onSave: ({ done?: boolean, autosave?: boolean, publish?: boolean }) => any,
|
||||||
innerRef: { current: any },
|
innerRef: { current: any },
|
||||||
children: React.Node,
|
children: React.Node,
|
||||||
|
policies: PoliciesStore,
|
||||||
t: TFunction,
|
t: TFunction,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
@ -104,10 +106,12 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
readOnly,
|
readOnly,
|
||||||
innerRef,
|
innerRef,
|
||||||
children,
|
children,
|
||||||
|
policies,
|
||||||
t,
|
t,
|
||||||
...rest
|
...rest
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const can = policies.abilities(document.id);
|
||||||
const { emoji } = parseTitle(title);
|
const { emoji } = parseTitle(title);
|
||||||
const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `));
|
const startsWithEmojiAndSpace = !!(emoji && title.startsWith(`${emoji} `));
|
||||||
const normalizedTitle =
|
const normalizedTitle =
|
||||||
@ -124,7 +128,9 @@ class DocumentEditor extends React.Component<Props> {
|
|||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
<span>{normalizedTitle}</span>{" "}
|
<span>{normalizedTitle}</span>{" "}
|
||||||
{!shareId && <StarButton document={document} size={32} />}
|
{(can.star || can.unstar) && (
|
||||||
|
<StarButton document={document} size={32} />
|
||||||
|
)}
|
||||||
</Title>
|
</Title>
|
||||||
) : (
|
) : (
|
||||||
<Title
|
<Title
|
||||||
@ -231,4 +237,6 @@ const Title = styled(Textarea)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default withTranslation()<DocumentEditor>(DocumentEditor);
|
export default withTranslation()<DocumentEditor>(
|
||||||
|
inject("policies")(DocumentEditor)
|
||||||
|
);
|
||||||
|
@ -76,7 +76,7 @@ function DocumentHeader({
|
|||||||
onSave({ done: true, publish: true });
|
onSave({ done: true, publish: true });
|
||||||
}, [onSave]);
|
}, [onSave]);
|
||||||
|
|
||||||
const isNew = document.isNew;
|
const isNew = document.isNewDocument;
|
||||||
const isTemplate = document.isTemplate;
|
const isTemplate = document.isTemplate;
|
||||||
const can = policies.abilities(document.id);
|
const can = policies.abilities(document.id);
|
||||||
const canShareDocument = auth.team && auth.team.sharing && can.share;
|
const canShareDocument = auth.team && auth.team.sharing && can.share;
|
||||||
|
@ -23,7 +23,6 @@ allow(User, ["star", "unstar"], Document, (user, document) => {
|
|||||||
if (document.archivedAt) return false;
|
if (document.archivedAt) return false;
|
||||||
if (document.deletedAt) return false;
|
if (document.deletedAt) return false;
|
||||||
if (document.template) return false;
|
if (document.template) return false;
|
||||||
if (!document.publishedAt) return false;
|
|
||||||
|
|
||||||
invariant(
|
invariant(
|
||||||
document.collection,
|
document.collection,
|
||||||
|
Reference in New Issue
Block a user