/* This file is part of the Notesnook project (https://notesnook.com/) Copyright (C) 2023 Streetwriters (Private) Limited This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import { InternalLink, TextSlice, VirtualizedGrouping, createInternalLink, highlightInternalLinks } from "@notesnook/core"; import { ContentBlock, ItemReference, Note } from "@notesnook/core/dist/types"; import { useThemeColors } from "@notesnook/theme"; import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import { ActivityIndicator, View } from "react-native"; import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList"; import create from "zustand"; import { db } from "../../../common/database"; import { useDBItem } from "../../../hooks/use-db-item"; import { eSendEvent, presentSheet } from "../../../services/event-manager"; import { useRelationStore } from "../../../stores/use-relation-store"; import { eOnLoadNote } from "../../../utils/events"; import { tabBarRef } from "../../../utils/global-refs"; import { SIZE } from "../../../utils/size"; import SheetProvider from "../../sheet-provider"; import { Button } from "../../ui/button"; import { IconButton } from "../../ui/icon-button"; import { PressableButton } from "../../ui/pressable"; import Paragraph from "../../ui/typography/paragraph"; export const useExpandedStore = create<{ expanded: { [id: string]: boolean; }; setExpanded: (id: string) => void; }>((set, get) => ({ expanded: {}, setExpanded(id: string) { set({ expanded: { ...get().expanded, [id]: !get().expanded[id] } }); } })); const ListBlockItem = ({ item, onSelectBlock }: { item: ContentBlock; onSelectBlock: () => void; }) => { const { colors } = useThemeColors(); return ( { onSelectBlock(); }} type={"transparent"} customStyle={{ flexDirection: "row", width: "100%", paddingLeft: 35, justifyContent: "flex-start", minHeight: 45, paddingHorizontal: 12 }} > {item.type.toUpperCase()} {item?.content.length > 200 ? item?.content.slice(0, 200) + "..." : item.content} ); }; const ListNoteInternalLink = ({ link, onSelect }: { link: { blockId: string; highlightedText: [TextSlice, TextSlice, TextSlice][]; }; onSelect: () => void; }) => { const { colors } = useThemeColors(); return ( { onSelect(); }} type={"transparent"} customStyle={{ flexDirection: "row", width: "100%", paddingLeft: 35, justifyContent: "flex-start", minHeight: 45 }} > {link.highlightedText.map((text) => ( {text.map((slice) => !slice.highlighted ? ( slice.text ) : ( {slice.text} ) )} ))} ); }; const ListNoteItem = ({ id, items, onSelect, reference, internalLinks, listType }: { id: number; items: VirtualizedGrouping | undefined; onSelect: (item: Note, blockId?: string) => void; reference: Note; internalLinks: MutableRefObject[] | undefined>; listType: "linkedNotes" | "referencedIn"; }) => { const { colors } = useThemeColors(); const [item] = useDBItem(id, "note", items); const expanded = useExpandedStore((state) => !item ? false : state.expanded[item.id] ); const [linkedBlocks, setLinkedBlocks] = useState([]); const [noteInternalLinks, setNoteInternalLinks] = useState< { blockId: string; highlightedText: [TextSlice, TextSlice, TextSlice][]; }[] >([]); const [loading, setLoading] = useState(false); useEffect(() => { if (item?.id && expanded) { (async () => { if (listType === "linkedNotes") { if (linkedBlocks.length) return; setLoading(true); if (!internalLinks.current) { internalLinks.current = await db.notes.internalLinks(reference.id); } const noteLinks = internalLinks.current.filter( (link) => link.id === item.id && link.params?.blockId ); if (noteLinks.length) { const blocks = await db.notes.contentBlocks(item.id); setLinkedBlocks( blocks.filter((block) => noteLinks.find((link) => block.id === link.params?.blockId) ) ); } } else { if (noteInternalLinks.length) return; setLoading(true); const blocks = await db.notes.contentBlocks(item.id); setNoteInternalLinks( blocks .filter((block) => block.content.includes(createInternalLink("note", reference.id)) ) .map((block) => { return { blockId: block?.id as string, highlightedText: highlightInternalLinks( block as ContentBlock, reference.id ) }; }) ); } setLoading(false); })(); } }, [ item?.id, expanded, linkedBlocks.length, internalLinks, reference.id, listType, noteInternalLinks.length ]); const renderBlock = React.useCallback( (block: ContentBlock) => ( { if (!item) return; onSelect(item, block.id); }} /> ), [item, onSelect] ); const renderInternalLink = React.useCallback( (link: { blockId: string; highlightedText: [TextSlice, TextSlice, TextSlice][]; }) => ( { if (!item) return; onSelect(item, link.blockId); }} /> ), [item, onSelect] ); return ( { if (!item) return; onSelect(item as Note); }} customStyle={{ flexDirection: "row", alignItems: "center", justifyContent: "flex-start", width: "100%", height: 45 }} > { if (!item?.id) return; useExpandedStore.getState().setExpanded(item?.id); }} top={0} left={0} bottom={0} right={0} customStyle={{ width: 35, height: 35 }} name={expanded ? "chevron-down" : "chevron-right"} /> {item?.title} {expanded && !item?.locked ? ( {loading ? ( ) : ( <> {listType === "linkedNotes" ? ( <> {linkedBlocks.length === 0 ? ( No blocks linked ) : ( linkedBlocks.map(renderBlock) )} ) : ( <> {noteInternalLinks.length === 0 ? ( No references found of this note ) : ( noteInternalLinks.map(renderInternalLink) )} )} )} ) : null} ); }; type ReferencesListProps = { item: { id: string; type: string }; close?: (ctx?: any) => void; }; export const ReferencesList = ({ item, close }: ReferencesListProps) => { const [tab, setTab] = useState(0); const updater = useRelationStore((state) => state.updater); const { colors } = useThemeColors(); const [items, setItems] = useState>(); const hasNoRelations = !items || items?.placeholders?.length === 0; const internalLinks = useRef[]>(); useEffect(() => { db.relations?.[tab === 0 ? "from" : "to"]?.( { id: item?.id, type: item?.type } as ItemReference, "note" ) .selector.sorted({ sortBy: "dateEdited", sortDirection: "desc" }) .then((items) => { setItems(items); }); }, [item?.id, item?.type, tab]); const renderNote = React.useCallback( ({ index }: any) => ( { console.log(note.id, blockId); eSendEvent(eOnLoadNote, { item: note, blockId: blockId }); tabBarRef.current?.goToPage(1); close?.(); }} reference={item as Note} internalLinks={internalLinks} listType={tab === 0 ? "linkedNotes" : "referencedIn"} /> ), [items, item, tab, close] ); return (