From cfa98578ea88a0b8f0d97c5c7d15aa98201e0d6f Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Thu, 7 Mar 2024 11:15:16 +0500 Subject: [PATCH] mobile: note linking --- .../app/components/sheets/link-note/index.tsx | 307 ++++++++++++++++++ apps/mobile/app/screens/editor/index.tsx | 31 +- packages/core/package-lock.json | 5 + packages/core/package.json | 3 + packages/core/src/collections/notes.ts | 12 + packages/core/src/content-types/tiptap.ts | 112 ++++--- packages/core/src/types.ts | 6 + packages/editor-mobile/src/App.tsx | 5 +- .../editor-mobile/src/components/editor.tsx | 131 ++++---- packages/editor-mobile/src/index.tsx | 6 +- packages/editor/src/hooks/use-editor.ts | 7 +- 11 files changed, 490 insertions(+), 135 deletions(-) create mode 100644 apps/mobile/app/components/sheets/link-note/index.tsx diff --git a/apps/mobile/app/components/sheets/link-note/index.tsx b/apps/mobile/app/components/sheets/link-note/index.tsx new file mode 100644 index 000000000..d63e43227 --- /dev/null +++ b/apps/mobile/app/components/sheets/link-note/index.tsx @@ -0,0 +1,307 @@ +/* +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 { ContentBlock, Note, VirtualizedGrouping } from "@notesnook/core"; +import { useThemeColors } from "@notesnook/theme"; +import React, { useEffect, useRef, useState } from "react"; +import { TextInput, View } from "react-native"; +import { FlatList } from "react-native-actions-sheet"; +import { db } from "../../../common/database"; +import { useDBItem } from "../../../hooks/use-db-item"; +import { presentSheet } from "../../../services/event-manager"; +import { SIZE } from "../../../utils/size"; +import Input from "../../ui/input"; +import { PressableButton } from "../../ui/pressable"; +import Paragraph from "../../ui/typography/paragraph"; +import { Button } from "../../ui/button"; + +const ListNoteItem = ({ + id, + items, + onSelectNote +}: { + id: any; + items: VirtualizedGrouping | undefined; + onSelectNote: any; +}) => { + const [item] = useDBItem(id, "note", items); + return ( + { + if (!item) return; + onSelectNote(item as Note); + }} + type={"transparent"} + customStyle={{ + paddingVertical: 12, + flexDirection: "row", + width: "100%", + justifyContent: "flex-start", + height: 50 + }} + > + + {item?.title} + + + ); +}; + +const ListBlockItem = ({ + item, + onSelectBlock +}: { + item: ContentBlock; + onSelectBlock: any; +}) => { + const { colors } = useThemeColors(); + return ( + { + onSelectBlock(item); + }} + type={"transparent"} + customStyle={{ + flexDirection: "row", + width: "100%", + justifyContent: "flex-start", + minHeight: 45 + }} + > + + + {item?.content.length > 200 + ? item?.content.slice(0, 200) + "..." + : item.content} + + + + + {item.type.toUpperCase()} + + + + + ); +}; + +export default function LinkNote() { + const { colors } = useThemeColors(); + const query = useRef(); + const [notes, setNotes] = useState>(); + const nodesRef = useRef([]); + const [nodes, setNodes] = useState([]); + + const inputRef = useRef(); + + const [selectedNote, setSelectedNote] = useState(); + const [selectedNodeId, setSelectedNodeId] = useState(); + + useEffect(() => { + db.notes.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => { + setNotes(notes); + }); + }, []); + + const onChange = async (value: string) => { + query.current = value; + if (!selectedNote) { + const notes = await db.lookup.notes(value).sorted(); + setNotes(notes); + } else { + if (value.startsWith("#")) { + const headingNodes = nodesRef.current.filter((n) => + n.type.match(/(h1|h2|h3|h4|h5|h6)/g) + ); + setNodes( + headingNodes.filter((n) => n.content.includes(value.slice(1))) + ); + } else { + setNodes(nodesRef.current.filter((n) => n.content.includes(value))); + } + } + }; + + const onSelectNote = async (note: Note) => { + setSelectedNote(note); + inputRef.current?.clear(); + setTimeout(async () => { + nodesRef.current = await db.notes.getBlocks(note.id); + setNodes(nodesRef.current); + }); + // Fetch and set note's nodes. + }; + + const onSelectBlock = (block: ContentBlock) => { + setSelectedNodeId(block.id); + }; + + return ( + + + { + onChange(value); + }} + /> + + {selectedNote ? ( + + + SELECTED NOTE + + { + setSelectedNote(undefined); + setSelectedNodeId(undefined); + setNodes([]); + }} + customStyle={{ + flexDirection: "row", + width: "100%", + justifyContent: "flex-start", + height: 45, + borderWidth: 1, + borderColor: colors.primary.accent, + paddingHorizontal: 12 + }} + type="grayAccent" + > + + {selectedNote?.title} + + + Tap to deselect + + + + + {nodes?.length > 0 ? ( + + LINK TO A SECTION + + ) : null} + + ) : null} + + + {selectedNote ? ( + ( + + )} + keyExtractor={(item) => item.id} + data={nodes} + /> + ) : ( + ( + + )} + data={notes?.placeholders} + /> + )} + + {selectedNote ? ( +