fix: Link preview and search should work on collection descriptions (#3355)

This commit is contained in:
Tom Moor
2022-04-09 19:00:56 -07:00
committed by GitHub
parent a47427de9e
commit 48fad5cfa0
4 changed files with 98 additions and 90 deletions

View File

@@ -1,15 +1,23 @@
import { formatDistanceToNow } from "date-fns";
import { deburr, sortBy } from "lodash";
import * as React from "react";
import { Optional } from "utility-types";
import embeds from "@shared/editor/embeds";
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
import { isInternalUrl } from "@shared/utils/urls";
import Document from "~/models/Document";
import ErrorBoundary from "~/components/ErrorBoundary";
import HoverPreview from "~/components/HoverPreview";
import type { Props as EditorProps, Editor as SharedEditor } from "~/editor";
import useDictionary from "~/hooks/useDictionary";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
import { NotFoundError } from "~/utils/errors";
import { uploadFile } from "~/utils/files";
import history from "~/utils/history";
import { isModKey } from "~/utils/keyboard";
import { isHash } from "~/utils/urls";
import DocumentBreadcrumb from "./DocumentBreadcrumb";
const LazyLoadedEditor = React.lazy(
() =>
@@ -38,8 +46,74 @@ export type Props = Optional<
function Editor(props: Props, ref: React.Ref<SharedEditor>) {
const { id, shareId } = props;
const { documents } = useStores();
const { showToast } = useToasts();
const dictionary = useDictionary();
const [
activeLinkEvent,
setActiveLinkEvent,
] = React.useState<MouseEvent | null>(null);
const handleLinkActive = React.useCallback((event: MouseEvent) => {
setActiveLinkEvent(event);
return false;
}, []);
const handleLinkInactive = React.useCallback(() => {
setActiveLinkEvent(null);
}, []);
const handleSearchLink = React.useCallback(
async (term: string) => {
if (isInternalUrl(term)) {
// search for exact internal document
const slug = parseDocumentSlug(term);
if (!slug) {
return [];
}
try {
const document = await documents.fetch(slug);
const time = formatDistanceToNow(Date.parse(document.updatedAt), {
addSuffix: true,
});
return [
{
title: document.title,
subtitle: `Updated ${time}`,
url: document.url,
},
];
} catch (error) {
// NotFoundError could not find document for slug
if (!(error instanceof NotFoundError)) {
throw error;
}
}
}
// default search for anything that doesn't look like a URL
const results = await documents.searchTitles(term);
return sortBy(
results.map((document: Document) => {
return {
title: document.title,
subtitle: <DocumentBreadcrumb document={document} onlyText />,
url: document.url,
};
}),
(document) =>
deburr(document.title)
.toLowerCase()
.startsWith(deburr(term).toLowerCase())
? -1
: 1
);
},
[documents]
);
const onUploadFile = React.useCallback(
async (file: File) => {
@@ -87,17 +161,28 @@ function Editor(props: Props, ref: React.Ref<SharedEditor>) {
return (
<ErrorBoundary reloadOnChunkMissing>
<LazyLoadedEditor
ref={ref}
uploadFile={onUploadFile}
onShowToast={showToast}
embeds={embeds}
dictionary={dictionary}
{...props}
onClickLink={onClickLink}
placeholder={props.placeholder || ""}
defaultValue={props.defaultValue || ""}
/>
<>
<LazyLoadedEditor
ref={ref}
uploadFile={onUploadFile}
onShowToast={showToast}
embeds={embeds}
dictionary={dictionary}
{...props}
onHoverLink={handleLinkActive}
onClickLink={onClickLink}
onSearchLink={handleSearchLink}
placeholder={props.placeholder || ""}
defaultValue={props.defaultValue || ""}
/>
{activeLinkEvent && !shareId && (
<HoverPreview
node={activeLinkEvent.target as HTMLAnchorElement}
event={activeLinkEvent}
onClose={handleLinkInactive}
/>
)}
</>
</ErrorBoundary>
);
}

View File

@@ -126,7 +126,7 @@ function HoverPreviewInternal({ node, onClose }: Props) {
function HoverPreview({ node, ...rest }: Props) {
const isMobile = useMobile();
if (!isMobile) {
if (isMobile) {
return null;
}
@@ -157,7 +157,7 @@ const Margin = styled.div`
const CardContent = styled.div`
overflow: hidden;
max-height: 350px;
max-height: 20em;
user-select: none;
`;