import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; import { uniqBy } from "lodash-es"; import { LinkIcon } from "lucide-react"; import React, { useContext, useState } from "react"; import { toast } from "react-hot-toast"; import useDebounce from "react-use/lib/useDebounce"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/Popover"; import { memoServiceClient } from "@/grpcweb"; import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts"; import useCurrentUser from "@/hooks/useCurrentUser"; import { MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service"; import { Memo } from "@/types/proto/api/v1/memo_service"; import { useTranslate } from "@/utils/i18n"; import { EditorRefActions } from "../Editor"; import { MemoEditorContext } from "../types"; interface Props { editorRef: React.RefObject; } const AddMemoRelationPopover = (props: Props) => { const { editorRef } = props; const t = useTranslate(); const context = useContext(MemoEditorContext); const user = useCurrentUser(); const [searchText, setSearchText] = useState(""); const [isFetching, setIsFetching] = useState(true); const [fetchedMemos, setFetchedMemos] = useState([]); const [selectedMemos, setSelectedMemos] = useState([]); const [embedded, setEmbedded] = useState(true); const [popoverOpen, setPopoverOpen] = useState(false); const filteredMemos = fetchedMemos.filter( (memo) => !selectedMemos.includes(memo) && memo.name !== context.memoName && !context.relationList.some((relation) => relation.relatedMemo === memo.name), ); useDebounce( async () => { setIsFetching(true); try { const filters = [`creator == "${user.name}"`, `row_status == "NORMAL"`]; if (searchText) { filters.push(`content_search == [${JSON.stringify(searchText)}]`); } const { memos } = await memoServiceClient.listMemos({ pageSize: DEFAULT_LIST_MEMOS_PAGE_SIZE, filter: filters.length > 0 ? filters.join(" && ") : undefined, }); setFetchedMemos(memos); } catch (error: any) { toast.error(error.details); console.error(error); } setIsFetching(false); }, 300, [searchText], ); const getHighlightedContent = (content: string) => { const index = content.toLowerCase().indexOf(searchText.toLowerCase()); if (index === -1) { return content; } let before = content.slice(0, index); if (before.length > 20) { before = "..." + before.slice(before.length - 20); } const highlighted = content.slice(index, index + searchText.length); let after = content.slice(index + searchText.length); if (after.length > 20) { after = after.slice(0, 20) + "..."; } return ( <> {before} {highlighted} {after} ); }; const addMemoRelations = async () => { // If embedded mode is enabled, embed the memo instead of creating a relation. if (embedded) { if (!editorRef.current) { toast.error("Failed to embed memo"); return; } const cursorPosition = editorRef.current.getCursorPosition(); const prevValue = editorRef.current.getContent().slice(0, cursorPosition); if (prevValue !== "" && !prevValue.endsWith("\n")) { editorRef.current.insertText("\n"); } for (const memo of selectedMemos) { editorRef.current.insertText(`![[memos/${memo.uid}]]\n`); } setTimeout(() => { editorRef.current?.scrollToCursor(); editorRef.current?.focus(); }); } else { context.setRelationList( uniqBy( [ ...selectedMemos.map((memo) => ({ memo: context.memoName || "", relatedMemo: memo.name, type: MemoRelation_Type.REFERENCE, })), ...context.relationList, ].filter((relation) => relation.relatedMemo !== context.memoName), "relatedMemo", ), ); } setSelectedMemos([]); setPopoverOpen(false); }; return (
setSearchText(value.trim())} getOptionKey={(memo) => memo.name} getOptionLabel={(memo) => memo.content} isOptionEqualToValue={(memo, value) => memo.name === value.name} renderOption={(props, memo) => (

{memo.displayTime?.toLocaleString()}

{searchText ? getHighlightedContent(memo.content) : memo.snippet}

)} renderTags={(memos) => memos.map((memo) => (

{memo.displayTime?.toLocaleString()}

{memo.content}
)) } onChange={(_, value) => setSelectedMemos(value)} />
setEmbedded(e.target.checked)} />
); }; export default AddMemoRelationPopover;