diff --git a/apps/web/src/components/properties/index.tsx b/apps/web/src/components/properties/index.tsx
index 9c343bffd..5c0cb2659 100644
--- a/apps/web/src/components/properties/index.tsx
+++ b/apps/web/src/components/properties/index.tsx
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import React, { PropsWithChildren } from "react";
+import React, { PropsWithChildren, useRef, useState } from "react";
import {
Pin,
StarOutline,
@@ -26,9 +26,11 @@ import {
SyncOff,
ArrowLeft,
Circle,
- Checkmark
+ Checkmark,
+ ChevronDown,
+ ChevronRight
} from "../icons";
-import { Flex, Text } from "@theme-ui/components";
+import { Box, Button, Flex, Text } from "@theme-ui/components";
import {
useEditorStore,
ReadonlyEditorSession,
@@ -40,12 +42,24 @@ import { store as noteStore } from "../../stores/note-store";
import { AnimatedFlex } from "../animated";
import Toggle from "./toggle";
import ScrollContainer from "../scroll-container";
-import { ResolvedItem, getFormattedDate, usePromise } from "@notesnook/common";
+import {
+ getFormattedDate,
+ usePromise,
+ ResolvedItem,
+ useResolvedItem
+} from "@notesnook/common";
import { ScopedThemeProvider } from "../theme-provider";
import { ListItemWrapper } from "../list-container/list-profiles";
import { VirtualizedList } from "../virtualized-list";
import { SessionItem } from "../session-item";
+import {
+ ContentBlock,
+ Note,
+ VirtualizedGrouping,
+ highlightInternalLinks
+} from "@notesnook/core";
import { VirtualizedTable } from "../virtualized-table";
+import { TextSlice } from "@notesnook/core/dist/utils/content-block";
const tools = [
{ key: "pin", property: "pinned", icon: Pin, label: "Pin" },
@@ -117,6 +131,7 @@ function EditorProperties(props: EditorPropertiesProps) {
sx={{
display: "flex",
position: "absolute",
+ top: 0,
right: 0,
zIndex: 1,
height: "100%",
@@ -185,6 +200,7 @@ function EditorProperties(props: EditorPropertiesProps) {
))}
+
@@ -196,6 +212,308 @@ function EditorProperties(props: EditorPropertiesProps) {
}
export default React.memo(EditorProperties);
+enum InternalLinksTabs {
+ LINKED_NOTES = 0,
+ REFERENCED_IN = 1
+}
+function InternalLinks({ noteId }: { noteId: string }) {
+ const [tabIndex, setTabIndex] = useState(InternalLinksTabs.LINKED_NOTES);
+ const [expandedId, setExpandedId] = useState();
+
+ const result = usePromise(() => {
+ const links =
+ tabIndex === InternalLinksTabs.LINKED_NOTES
+ ? db.relations.from({ id: noteId, type: "note" }, "note")
+ : db.relations.to({ id: noteId, type: "note" }, "note");
+ return links.selector.sorted(db.settings.getGroupOptions("notes"));
+ }, [tabIndex]);
+
+ return (
+
+
+ {["Linked notes", "Referenced in"].map((title, index) => (
+
+ ))}
+
+
+ {result.status === "fulfilled" && (
+ result.value.key(index)}
+ items={result.value.placeholders}
+ context={{
+ items: result.value,
+ tabIndex,
+ noteId,
+ isExpanded: (id) => expandedId === id,
+ toggleExpand: (id) =>
+ setExpandedId((s) => (s === id ? undefined : id))
+ }}
+ renderItem={InternalLinkItem}
+ />
+ )}
+
+ );
+}
+
+function InternalLinkItem({
+ index,
+ context
+}: {
+ item: boolean;
+ index: number;
+ context: {
+ items: VirtualizedGrouping;
+ tabIndex: InternalLinksTabs;
+ noteId: string;
+ isExpanded: (id: string) => boolean;
+ toggleExpand: (id: string) => void;
+ };
+}) {
+ const { items, tabIndex, noteId, isExpanded, toggleExpand } = context;
+ const item = useResolvedItem({ items, index });
+
+ if (!item || item.item.type !== "note") return null;
+
+ if (tabIndex === InternalLinksTabs.LINKED_NOTES)
+ return (
+ toggleExpand(item.item.id)}
+ />
+ );
+ return (
+ toggleExpand(item.item.id)}
+ />
+ );
+}
+function LinkedNote({
+ item,
+ noteId,
+ isExpanded,
+ toggleExpand
+}: {
+ item: Note;
+ noteId: string;
+ toggleExpand: () => void;
+ isExpanded: boolean;
+}) {
+ const [blocks, setBlocks] = useState([]);
+
+ return (
+ <>
+
+ {isExpanded
+ ? blocks.map((block) => (
+
+ ))
+ : null}
+ >
+ );
+}
+
+function ReferencedIn({
+ item,
+ noteId,
+ isExpanded,
+ toggleExpand
+}: {
+ item: Note;
+ noteId: string;
+ toggleExpand: () => void;
+ isExpanded: boolean;
+}) {
+ const [blocks, setBlocks] = useState<
+ { id: string; links: [TextSlice, TextSlice, TextSlice][] }[]
+ >([]);
+
+ return (
+ <>
+
+ {isExpanded
+ ? blocks.map((block) => (
+ <>
+ {block.links.map((link, index) => (
+
+ ))}
+ >
+ ))
+ : null}
+ >
+ );
+}
+
function Colors({ noteId, color }: { noteId: string; color?: string }) {
const result = usePromise(() => db.colors.all.items(), [color]);
return (
@@ -268,6 +586,7 @@ function Notebooks({ noteId }: { noteId: string }) {
);
}
+
function Reminders({ noteId }: { noteId: string }) {
const result = usePromise(() =>
db.relations
diff --git a/apps/web/src/dialogs/note-linking-dialog.tsx b/apps/web/src/dialogs/note-linking-dialog.tsx
index bae4c69bd..f22a4d46e 100644
--- a/apps/web/src/dialogs/note-linking-dialog.tsx
+++ b/apps/web/src/dialogs/note-linking-dialog.tsx
@@ -162,7 +162,7 @@ export default function NoteLinkingDialog(props: NoteLinkingDialogProps) {
sx={{ p: 1, width: "100%", textAlign: "left" }}
onClick={async () => {
setSelectedNote(note);
- setBlocks(await db.notes.getBlocks(note.id));
+ setBlocks(await db.notes.contentBlocks(note.id));
}}
>
{note.title}