/* This file is part of the Notesnook project (https://notesnook.com/) * * Copyright (C) 2022 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 React, { createRef, useEffect, useState } from "react"; import { Keyboard, TouchableOpacity, View } from "react-native"; import { FlatList } from "react-native-gesture-handler"; import { notesnook } from "../../../../e2e/test.ids"; import { db } from "../../../common/database"; import { eSubscribeEvent, eUnSubscribeEvent, ToastEvent } from "../../../services/event-manager"; import Navigation from "../../../services/navigation"; import SearchService from "../../../services/search"; import { useNotebookStore } from "../../../stores/use-notebook-store"; import { useSelectionStore } from "../../../stores/use-selection-store"; import { useThemeStore } from "../../../stores/use-theme-store"; import { getTotalNotes } from "../../../utils"; import { eOpenMoveNoteDialog } from "../../../utils/events"; import layoutmanager from "../../../utils/layout-manager"; import { SIZE } from "../../../utils/size"; import { Dialog } from "../../dialog"; import DialogHeader from "../../dialog/dialog-header"; import { presentDialog } from "../../dialog/functions"; import { Button } from "../../ui/button"; import { IconButton } from "../../ui/icon-button"; import Input from "../../ui/input"; import { PressableButton } from "../../ui/pressable"; import SheetWrapper from "../../ui/sheet"; import Heading from "../../ui/typography/heading"; import Paragraph from "../../ui/typography/paragraph"; let newNotebookTitle = null; const notebookInput = createRef(); const actionSheetRef = createRef(); const AddToNotebookSheet = () => { const [visible, setVisible] = useState(false); const [note, setNote] = useState(null); function open(note) { setNote(note); setVisible(true); actionSheetRef.current?.setModalVisible(true); } useEffect(() => { eSubscribeEvent(eOpenMoveNoteDialog, open); return () => { eUnSubscribeEvent(eOpenMoveNoteDialog, open); }; }, []); const _onClose = () => { setVisible(false); newNotebookTitle = null; setNote(null); Navigation.queueRoutesForUpdate( "Notes", "Favorites", "ColoredNotes", "TaggedNotes", "TopicNotes", "Notebooks", "Notebook" ); }; return !visible ? null : ( ); }; export default AddToNotebookSheet; const MoveNoteComponent = ({ note }) => { const colors = useThemeStore((state) => state.colors); const notebooks = useNotebookStore((state) => state.notebooks.filter((n) => n?.type === "notebook") ); const selectedItemsList = useSelectionStore( (state) => state.selectedItemsList ); const setNotebooks = useNotebookStore((state) => state.setNotebooks); const [expanded, setExpanded] = useState(""); const [notebookInputFocused, setNotebookInputFocused] = useState(false); const [noteExists, setNoteExists] = useState([]); const addNewNotebook = async () => { if (!newNotebookTitle || newNotebookTitle.trim().length === 0) return ToastEvent.show({ heading: "Notebook title is required", type: "error", context: "local" }); let id = await db.notebooks.add({ title: newNotebookTitle, description: null, topics: [], id: null }); console.log("added notebook id", id); setExpanded(id); openAddTopicDialog(db.notebooks.notebook(id).data); notebookInput.current?.clear(); notebookInput.current?.blur(); setNotebooks(); updateNoteExists(); }; const addNewTopic = async (value, item) => { if (!value || value.trim().length === 0) { ToastEvent.show({ heading: "Topic title is required", type: "error", context: "local" }); return false; } console.log(item.id); await db.notebooks.notebook(item.id).topics.add(value); setNotebooks(); updateNoteExists(); return true; }; const handlePress = async (item) => { let noteIds = selectedItemsList.length > 0 ? selectedItemsList.map((n) => n.id) : [note?.id]; if (getCount(item)) { await db.notebooks .notebook(item.notebookId) .topics.topic(item.id) .delete(...noteIds); } else { await db.notes.move( { topic: item.id, id: item.notebookId }, ...noteIds ); } Navigation.queueRoutesForUpdate( "Notes", "Favorites", "ColoredNotes", "TaggedNotes", "TopicNotes" ); setNotebooks(); updateNoteExists(); SearchService.updateAndSearch(); }; useEffect(() => { updateNoteExists(); }, []); const updateNoteExists = () => { if (!note?.id && selectedItemsList?.length === 0) return; let notes = selectedItemsList.length > 0 ? selectedItemsList.map((n) => n.id) : [note?.id]; let ids = []; let notebooks = db.notebooks.all; for (let i = 0; i < notebooks.length; i++) { if (notebooks[i].topics) { for (let t = 0; t < notebooks[i].topics.length; t++) { let topic = notebooks[i].topics[t]; if (topic.type !== "topic") continue; for (let id of notes) { if (topic.notes.indexOf(id) > -1) { console.log("found", ids.indexOf(notebooks[i].id)); if (ids.indexOf(notebooks[i].id) === -1) { ids.push(notebooks[i].id); } if (ids.indexOf(topic.id) === -1) ids.push(topic.id); } } } } } console.log("ids: ", ids); setNoteExists(ids); }; const openAddTopicDialog = (item) => { presentDialog({ context: "move_note", input: true, inputPlaceholder: "Enter title", title: "New topic", paragraph: "Add a new topic in " + item.title, positiveText: "Add", positivePress: (value) => { return addNewTopic(value, item); } }); }; const getCount = (topic) => { if (!topic) return; let notes = selectedItemsList.length > 0 ? selectedItemsList.map((n) => n.id) : [note?.id]; let count = 0; for (let id of notes) { if (topic.notes.indexOf(id) > -1) { count++; } } return count > 0 && notes.length > 0 ? `${count} of ${notes.length} selected notes exist in this topic. (Tap to remove)` : null; }; return ( <> { Keyboard.dismiss(); }} /> { actionSheetRef.current?.handleChildScrollEnd(); }} keyboardShouldPersistTaps="always" keyboardDismissMode="none" data={notebooks} ListFooterComponent={ } ListHeaderComponent={ { newNotebookTitle = value; }} testID={notesnook.ids.dialogs.addTo.addNotebook} blurOnSubmit={false} onFocusInput={() => { setNotebookInputFocused(true); }} onBlurInput={() => { setNotebookInputFocused(false); }} button={{ icon: "check", color: notebookInputFocused ? colors.accent : colors.icon, onPress: addNewNotebook }} onSubmit={addNewNotebook} placeholder="Create a new notebook" /> } style={{ paddingHorizontal: 12 }} renderItem={({ item }) => ( { if (!item.topics || item.topics.length === 0) { setExpanded(item.id); openAddTopicDialog(item); return; } layoutmanager.withAnimation(200); setExpanded(item.id === expanded ? null : item.id); setNotebookInputFocused(false); }} type="grayBg" customStyle={{ height: 50, width: "100%", borderRadius: 5, alignItems: "flex-start" }} > -1 ? colors.accent : null } size={SIZE.md} > {item.title} {item.topics?.length > 0 ? ( {getTotalNotes(item) + " notes" + " & "} {item.topics.length === 1 ? item.topics.length + " topic" : item.topics.length + " topics"} ) : null} { if (expanded !== item.id) { setExpanded(item.id); return; } layoutmanager.withAnimation(200); setExpanded(item.id); openAddTopicDialog(item); }} /> {expanded === item.id ? ( t.type === "topic")} keyboardShouldPersistTaps="always" keyboardDismissMode="none" onMomentumScrollEnd={() => { actionSheetRef.current?.handleChildScrollEnd(); }} style={{ width: "100%", alignSelf: "flex-end", maxHeight: 500 }} renderItem={({ item, index }) => ( handlePress(item, index)} type="gray" customStyle={{ minHeight: 50, borderTopWidth: index === 0 ? 0 : 1, borderTopColor: index === 0 ? null : colors.nav, width: "100%", borderRadius: 0, alignItems: "center", flexDirection: "row", paddingHorizontal: 12, justifyContent: "space-between", paddingVertical: 12 }} > {item.title} {item.notes.length + " notes"} {getCount(item) ? ( {getCount(item)} ) : null} {noteExists.indexOf(item.id) > -1 ? (