import { uniqBy } from "lodash-es"; import { LinkIcon } from "lucide-react"; import { useContext, useState } from "react"; import { toast } from "react-hot-toast"; import useDebounce from "react-use/lib/useDebounce"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { memoServiceClient } from "@/grpcweb"; import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts"; import useCurrentUser from "@/hooks/useCurrentUser"; import { extractUserIdFromName } from "@/store/common"; import { Memo, MemoRelation_Memo, MemoRelation_Type } from "@/types/proto/api/v1/memo_service"; import { useTranslate } from "@/utils/i18n"; import { MemoEditorContext } from "../types"; const AddMemoRelationPopover = () => { const t = useTranslate(); const context = useContext(MemoEditorContext); const user = useCurrentUser(); const [searchText, setSearchText] = useState(""); const [isFetching, setIsFetching] = useState(true); const [fetchedMemos, setFetchedMemos] = useState([]); const [popoverOpen, setPopoverOpen] = useState(false); const filteredMemos = fetchedMemos.filter( (memo) => memo.name !== context.memoName && !context.relationList.some((relation) => relation.relatedMemo?.name === memo.name), ); useDebounce( async () => { if (!popoverOpen) return; setIsFetching(true); try { const conditions = [`creator_id == ${extractUserIdFromName(user.name)}`]; if (searchText) { conditions.push(`content.contains("${searchText}")`); } const { memos } = await memoServiceClient.listMemos({ filter: conditions.join(" && "), pageSize: DEFAULT_LIST_MEMOS_PAGE_SIZE, }); setFetchedMemos(memos); } catch (error: any) { toast.error(error.details); console.error(error); } setIsFetching(false); }, 300, [popoverOpen, 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 (memo: Memo) => { context.setRelationList( uniqBy( [ { memo: MemoRelation_Memo.fromPartial({ name: memo.name }), relatedMemo: MemoRelation_Memo.fromPartial({ name: memo.name }), type: MemoRelation_Type.REFERENCE, }, ...context.relationList, ].filter((relation) => relation.relatedMemo !== context.memoName), "relatedMemo", ), ); setPopoverOpen(false); }; return (

{t("tooltip.link-memo")}

{/* Search and selection interface */}
setSearchText(e.target.value)} className="mb-2" />
{filteredMemos.length === 0 ? (
{isFetching ? "Loading..." : t("reference.no-memos-found")}
) : ( filteredMemos.map((memo) => (
{ addMemoRelations(memo); }} >

{memo.displayTime?.toLocaleString()}

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

)) )}
); }; export default AddMemoRelationPopover;