Files
outline/app/components/Editor.tsx
Tom Moor 631d600920 feat: File attachments (#3031)
* stash

* refactor, working in non-collab + collab editor

* attachment styling

* Avoid crypto require in browser

* AttachmentIcon, handling unknown types

* Do not allow attachment creation for file sizes over limit

* Allow image as file attachment

* Upload placeholder styling

* lint

* Refactor: Do not use placeholder for file attachmentuploads

* Add loading spinner

* fix: Extra paragraphs around attachments on insert

* Bump editor

* fix build error

* Remove attachment placeholder when upload fails

* Remove unused styles

* fix: Attachments on shared pages

* Merge fixes
2022-03-06 13:58:58 -08:00

112 lines
2.6 KiB
TypeScript

import * as React from "react";
import { Optional } from "utility-types";
import embeds from "@shared/editor/embeds";
import { isInternalUrl } from "@shared/utils/urls";
import ErrorBoundary from "~/components/ErrorBoundary";
import { Props as EditorProps } from "~/editor";
import useDictionary from "~/hooks/useDictionary";
import useToasts from "~/hooks/useToasts";
import { uploadFile } from "~/utils/files";
import history from "~/utils/history";
import { isModKey } from "~/utils/keyboard";
import { isHash } from "~/utils/urls";
const SharedEditor = React.lazy(
() =>
import(
/* webpackChunkName: "shared-editor" */
"~/editor"
)
);
export type Props = Optional<
EditorProps,
| "placeholder"
| "defaultValue"
| "onClickLink"
| "embeds"
| "dictionary"
| "onShowToast"
> & {
shareId?: string | undefined;
embedsDisabled?: boolean;
grow?: boolean;
onSynced?: () => Promise<void>;
onPublish?: (event: React.MouseEvent) => any;
};
function Editor(props: Props, ref: React.Ref<any>) {
const { id, shareId } = props;
const { showToast } = useToasts();
const dictionary = useDictionary();
const onUploadFile = React.useCallback(
async (file: File) => {
const result = await uploadFile(file, {
documentId: id,
});
return result.url;
},
[id]
);
const onClickLink = React.useCallback(
(href: string, event: MouseEvent) => {
// on page hash
if (isHash(href)) {
window.location.href = href;
return;
}
if (isInternalUrl(href) && !isModKey(event) && !event.shiftKey) {
// relative
let navigateTo = href;
// probably absolute
if (href[0] !== "/") {
try {
const url = new URL(href);
navigateTo = url.pathname + url.hash;
} catch (err) {
navigateTo = href;
}
}
if (shareId) {
navigateTo = `/share/${shareId}${navigateTo}`;
}
history.push(navigateTo);
} else if (href) {
window.open(href, "_blank");
}
},
[shareId]
);
const onShowToast = React.useCallback(
(message: string) => {
showToast(message);
},
[showToast]
);
return (
<ErrorBoundary reloadOnChunkMissing>
<SharedEditor
ref={ref}
uploadFile={onUploadFile}
onShowToast={onShowToast}
embeds={embeds}
dictionary={dictionary}
{...props}
onClickLink={onClickLink}
placeholder={props.placeholder || ""}
defaultValue={props.defaultValue || ""}
/>
</ErrorBoundary>
);
}
export default React.forwardRef(Editor);