/* 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 React, { createRef } from "react"; import { Keyboard, StyleSheet, TextInput, TouchableOpacity, View } from "react-native"; import { notesnook } from "../../../../e2e/test.ids"; import { db } from "../../../common/database"; import { DDS } from "../../../services/device-detection"; import { presentSheet, ToastEvent } from "../../../services/event-manager"; import Navigation from "../../../services/navigation"; import { useMenuStore } from "../../../stores/use-menu-store"; import { useRelationStore } from "../../../stores/use-relation-store"; import { ph, pv, SIZE } from "../../../utils/size"; import { sleep } from "../../../utils/time"; import DialogHeader from "../../dialog/dialog-header"; import { Button } from "../../ui/button"; import { IconButton } from "../../ui/icon-button"; import Input from "../../ui/input"; import Seperator from "../../ui/seperator"; import { MoveNotes } from "../move-notes/movenote"; import { FlatList } from "react-native-actions-sheet"; let refs = []; export class AddNotebookSheet extends React.Component { constructor(props) { super(props); refs = []; this.state = { notebook: props.notebook, topics: props.notebook?.topics?.map((item) => { return item.title; }) || [], description: null, titleFocused: false, descFocused: false, count: 0, topicInputFocused: false, editTopic: false, loading: false }; this.title = props.notebook?.title; this.description = props.notebook?.description; this.listRef; this.prevItem = null; this.prevIndex = null; this.currentSelectedInput = null; this.id = props.notebook?.id; this.backPressCount = 0; this.currentInputValue = null; this.titleRef; this.descriptionRef; this.topicsToDelete = []; this.hiddenInput = createRef(); this.topicInputRef = createRef(); this.addingTopic = false; this.actionSheetRef = props.actionSheetRef; } componentWillUnmount() { refs = []; } componentDidMount() { sleep(300).then(() => { !this.state.notebook && this.titleRef?.focus(); }); } close = () => { refs = []; this.props.close(); }; onDelete = (index) => { let { topics } = this.state; let prevTopics = topics; refs = []; prevTopics.splice(index, 1); let edit = this.state.notebook; if (edit && edit.id) { let topicToDelete = edit.topics[index]; if (topicToDelete) { this.topicsToDelete.push(topicToDelete.id); } } let nextTopics = [...prevTopics]; if (this.prevIndex === index) { this.prevIndex = null; this.prevItem = null; this.currentInputValue = null; this.topicInputRef.current?.setNativeProps({ text: null }); } this.setState({ topics: nextTopics }); }; addNewNotebook = async () => { this.setState({ loading: true }); let { topics, notebook } = this.state; if (!this.title || this.title?.trim().length === 0) { ToastEvent.show({ heading: "Notebook title is required", type: "error", context: "local" }); this.setState({ loading: false }); return; } let toEdit = null; if (notebook) { toEdit = db.notebooks.notebook(notebook.id).data; } let prevTopics = [...topics]; if (this.currentInputValue && this.currentInputValue.trim().length !== 0) { if (this.prevItem != null) { prevTopics[this.prevIndex] = this.currentInputValue; } else { prevTopics.push(this.currentInputValue); this.currentInputValue = null; } } let newNotebookId = null; if (notebook) { if (this.topicsToDelete?.length > 0) { await db.notebooks .notebook(toEdit.id) .topics.delete(...this.topicsToDelete); toEdit = db.notebooks.notebook(toEdit.id).data; } await db.notebooks.add({ title: this.title, description: this.description, id: notebook.id }); let nextTopics = toEdit.topics.map((topic, index) => { let copy = { ...topic }; copy.title = prevTopics[index]; return copy; }); prevTopics.forEach((title, index) => { if (!nextTopics[index]) { nextTopics.push(title); } }); await db.notebooks.notebook(toEdit.id).topics.add(...nextTopics); } else { newNotebookId = await db.notebooks.add({ title: this.title, description: this.description, topics: prevTopics, id: null }); } useMenuStore.getState().setMenuPins(); Navigation.queueRoutesForUpdate( "Notes", "ColoredNotes", "TaggedNotes", "TopicNotes", "Notebooks", "Notebook" ); useRelationStore.getState().update(); MoveNotes.present(db.notebooks.notebook(newNotebookId).data); }; onSubmit = (forward = true) => { this.hiddenInput.current?.focus(); let willFocus = true; let { topics } = this.state; if (!this.currentInputValue || this.currentInputValue?.trim().length === 0) return; let prevTopics = [...topics]; if (this.prevItem === null) { prevTopics.push(this.currentInputValue); this.setState({ topics: prevTopics }); setTimeout(() => { this.listRef.scrollToEnd({ animated: true }); }, 30); this.currentInputValue = null; } else { prevTopics[this.prevIndex] = this.currentInputValue; this.setState({ topics: prevTopics }); this.currentInputValue = null; if (this.state.editTopic) { this.topicInputRef.current?.blur(); Keyboard.dismiss(); this.setState({ editTopic: false }); willFocus = false; } this.prevItem = null; this.prevIndex = null; this.currentInputValue = null; if (forward) { setTimeout(() => { this.listRef.scrollToEnd({ animated: true }); }, 30); } } this.topicInputRef.current?.setNativeProps({ text: "" }); willFocus && this.topicInputRef.current?.focus(); }; render() { const { colors } = this.props; const { topics, topicInputFocused, notebook } = this.state; return ( (this.titleRef = ref)} testID={notesnook.ids.dialogs.notebook.inputs.title} onChangeText={(value) => { this.title = value; }} placeholder="Enter a title" onSubmit={() => { this.descriptionRef.focus(); }} returnKeyLabel="Next" returnKeyType="next" defaultValue={notebook ? notebook.title : null} /> (this.descriptionRef = ref)} testID={notesnook.ids.dialogs.notebook.inputs.description} onChangeText={(value) => { this.description = value; }} placeholder="Describe your notebook." onSubmit={() => { this.topicInputRef.current?.focus(); }} returnKeyLabel="Next" returnKeyType="next" defaultValue={notebook ? notebook.description : null} /> { this.currentInputValue = value; if (this.prevItem !== null) { refs[this.prevIndex].setNativeProps({ text: value, style: { borderBottomColor: colors.accent } }); } }} returnKeyLabel="Done" returnKeyType="done" onSubmit={() => { this.onSubmit(); }} blurOnSubmit={false} button={{ testID: "topic-add-button", icon: this.state.editTopic ? "check" : "plus", onPress: this.onSubmit, color: topicInputFocused ? colors.accent : colors.icon }} placeholder="Add a topic" /> (this.listRef = ref)} nestedScrollEnabled keyExtractor={(item, index) => item + index.toString()} keyboardShouldPersistTaps="always" keyboardDismissMode="interactive" ListFooterComponent={} renderItem={({ item, index }) => ( { this.prevIndex = index; this.prevItem = item; this.topicInputRef.current?.setNativeProps({ text: item }); this.topicInputRef.current?.focus(); this.currentInputValue = item; this.setState({ editTopic: true }); }} onDelete={this.onDelete} index={index} colors={colors} /> )} />