diff --git a/app/components/Editor.tsx b/app/components/Editor.tsx index c5f3e41bd..2fb0c45f0 100644 --- a/app/components/Editor.tsx +++ b/app/components/Editor.tsx @@ -50,7 +50,7 @@ export type Props = Optional< previewsDisabled?: boolean; onHeadingsChange?: (headings: Heading[]) => void; onSynced?: () => Promise; - onPublish?: (event: React.MouseEvent) => any; + onPublish?: (event: React.MouseEvent) => void; editorStyle?: React.CSSProperties; }; @@ -138,7 +138,7 @@ function Editor(props: Props, ref: React.RefObject | null) { : 1 ); }, - [documents] + [locale, documents] ); const handleUploadFile = React.useCallback( diff --git a/app/editor/index.tsx b/app/editor/index.tsx index 9bd1218a9..9d98cb318 100644 --- a/app/editor/index.tsx +++ b/app/editor/index.tsx @@ -23,6 +23,7 @@ import { import { Decoration, EditorView, NodeViewConstructor } from "prosemirror-view"; import * as React from "react"; import styled, { css, DefaultTheme, ThemeProps } from "styled-components"; +import insertFiles from "@shared/editor/commands/insertFiles"; import Styles from "@shared/editor/components/Styles"; import { EmbedDescriptor } from "@shared/editor/embeds"; import Extension, { CommandFactory } from "@shared/editor/lib/Extension"; @@ -584,6 +585,25 @@ export class Editor extends React.PureComponent< window?.getSelection()?.removeAllRanges(); }; + /** + * Insert files at the current selection. + * = + * @param event The source event + * @param files The files to insert + * @returns True if the files were inserted + */ + public insertFiles = ( + event: React.ChangeEvent, + files: File[] + ) => + insertFiles( + this.view, + event, + this.view.state.selection.to, + files, + this.props + ); + /** * Returns true if the trimmed content of the editor is an empty string. * diff --git a/app/scenes/Document/components/CommentForm.tsx b/app/scenes/Document/components/CommentForm.tsx index bde685bb6..6eae5663d 100644 --- a/app/scenes/Document/components/CommentForm.tsx +++ b/app/scenes/Document/components/CommentForm.tsx @@ -1,16 +1,22 @@ import { m } from "framer-motion"; import { action } from "mobx"; import { observer } from "mobx-react"; +import { ImageIcon } from "outline-icons"; import * as React from "react"; import { useTranslation } from "react-i18next"; +import { VisuallyHidden } from "reakit"; import { toast } from "sonner"; +import { useTheme } from "styled-components"; import { v4 as uuidv4 } from "uuid"; -import { CommentValidation } from "@shared/validations"; +import { getEventFiles } from "@shared/utils/files"; +import { AttachmentValidation, CommentValidation } from "@shared/validations"; import Comment from "~/models/Comment"; import Avatar from "~/components/Avatar"; import ButtonSmall from "~/components/ButtonSmall"; import { useDocumentContext } from "~/components/DocumentContext"; import Flex from "~/components/Flex"; +import NudeButton from "~/components/NudeButton"; +import Tooltip from "~/components/Tooltip"; import type { Editor as SharedEditor } from "~/editor"; import useCurrentUser from "~/hooks/useCurrentUser"; import useOnClickOutside from "~/hooks/useOnClickOutside"; @@ -64,6 +70,8 @@ function CommentForm({ const editorRef = React.useRef(null); const [forceRender, setForceRender] = React.useState(0); const [inputFocused, setInputFocused] = React.useState(autoFocus); + const file = React.useRef(null); + const theme = useTheme(); const { t } = useTranslation(); const { comments } = useStores(); const user = useCurrentUser(); @@ -188,6 +196,23 @@ function CommentForm({ onBlur?.(); }; + const handleFilePicked = (event: React.ChangeEvent) => { + event.stopPropagation(); + event.preventDefault(); + + const files = getEventFiles(event); + if (!files.length) { + return; + } + editorRef.current?.insertFiles(event, files); + }; + + const handleImageUpload = (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + file.current?.click(); + }; + // Focus the editor when it's a new comment just mounted, after a delay as the // editor is mounted within a fade transition. React.useEffect(() => { @@ -227,6 +252,15 @@ function CommentForm({ {...presence} {...rest} > + + + {(inputFocused || data) && ( - - - {thread && !thread.isNew ? t("Reply") : t("Post")} - - - {t("Cancel")} - + + + + {thread && !thread.isNew ? t("Reply") : t("Post")} + + + {t("Cancel")} + + + + + + + )} diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 10be81c4b..07671e2e1 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -500,6 +500,7 @@ "Reply": "Reply", "Post": "Post", "Cancel": "Cancel", + "Upload image": "Upload image", "No comments yet": "No comments yet", "Error updating comment": "Error updating comment", "Images are still uploading.\nAre you sure you want to discard them?": "Images are still uploading.\nAre you sure you want to discard them?",