mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-02 12:14:48 +02:00
mobile: migrate share extension
This commit is contained in:
committed by
Abdullah Atta
parent
62878c4ae7
commit
a646450b05
@@ -28,31 +28,35 @@ import { eClearEditor } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
|
||||
export default function Notebooks({ note, close, full }) {
|
||||
const { colors } = useThemeColors();
|
||||
async function getNotebooks(item) {
|
||||
let filteredNotebooks = [];
|
||||
const relations = await db.relations.to(note, "notebook").resolve();
|
||||
filteredNotebooks.push(relations);
|
||||
if (!item.notebooks || item.notebooks.length < 1) return filteredNotebooks;
|
||||
return filteredNotebooks;
|
||||
async function getNotebooks() {
|
||||
let filteredNotebooks = await db.relations.to(note, "notebook").resolve();
|
||||
return filteredNotebooks || [];
|
||||
}
|
||||
const [noteNotebooks, setNoteNotebooks] = useState([]);
|
||||
useEffect(() => {
|
||||
getNotebooks().then((notebooks) => setNoteNotebooks(notebooks));
|
||||
});
|
||||
|
||||
const navigateNotebook = (id) => {
|
||||
let item = db.notebooks.notebook(id)?.data;
|
||||
const navigateNotebook = async (id) => {
|
||||
let item = await db.notebooks.notebook(id);
|
||||
if (!item) return;
|
||||
NotebookScreen.navigate(item, true);
|
||||
};
|
||||
|
||||
const renderItem = (item) => (
|
||||
<View
|
||||
<PressableButton
|
||||
key={item.id}
|
||||
style={{
|
||||
onPress={() => {
|
||||
navigateNotebook(item.id);
|
||||
eSendEvent(eClearEditor);
|
||||
close();
|
||||
}}
|
||||
type={full ? "transparent" : "grayBg"}
|
||||
customStyle={{
|
||||
justifyContent: "flex-start",
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
@@ -60,10 +64,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
flexShrink: 1,
|
||||
flexGrow: 1,
|
||||
padding: 6,
|
||||
borderWidth: full ? 0 : 1,
|
||||
borderColor: colors.primary.background,
|
||||
borderRadius: 10,
|
||||
backgroundColor: full ? "transparent" : colors.secondary.background,
|
||||
minHeight: 42
|
||||
}}
|
||||
>
|
||||
@@ -81,15 +82,10 @@ export default function Notebooks({ note, close, full }) {
|
||||
maxWidth: "50%"
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
onPress={() => {
|
||||
navigateNotebook(item.id);
|
||||
eSendEvent(eClearEditor);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
</View>
|
||||
</PressableButton>
|
||||
);
|
||||
|
||||
return noteNotebooks.length === 0 ? null : (
|
||||
@@ -106,7 +102,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
|
||||
{noteNotebooks.length > 1 && !full ? (
|
||||
<Button
|
||||
title={`See all linked notebooks`}
|
||||
title={`View all linked notebooks`}
|
||||
fontSize={SIZE.xs}
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
|
||||
@@ -70,34 +70,26 @@ async function createNotes(bundle) {
|
||||
if (!bundle.notebooks || !bundle.notebooks.length) {
|
||||
const defaultNotebook = db.settings?.getDefaultNotebook();
|
||||
if (defaultNotebook) {
|
||||
if (!defaultNotebook.topic) {
|
||||
db.relations.add(
|
||||
{ type: "notebook", id: defaultNotebook.id },
|
||||
{ id, type: "note" }
|
||||
);
|
||||
} else {
|
||||
db.notes.addToNotebook(
|
||||
{
|
||||
id: defaultNotebook?.id,
|
||||
topic: defaultNotebook.topic
|
||||
},
|
||||
id
|
||||
);
|
||||
}
|
||||
await db.notes.addToNotebook(defaultNotebook, id);
|
||||
}
|
||||
} else {
|
||||
for (const item of bundle.notebooks) {
|
||||
if (item.type === "notebook") {
|
||||
db.relations.add(item, { id, type: "note" });
|
||||
} else {
|
||||
db.notes.addToNotebook(
|
||||
{
|
||||
id: item.notebookId,
|
||||
topic: item.id
|
||||
},
|
||||
id
|
||||
);
|
||||
}
|
||||
await db.notes.addToNotebook(item, id);
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.tags) {
|
||||
for (const tagId of bundle.tags) {
|
||||
await db.relations.add(
|
||||
{
|
||||
type: "tag",
|
||||
id: tagId
|
||||
},
|
||||
{
|
||||
id: id,
|
||||
type: "note"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,8 @@ describe("VAULT", () => {
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await elementById(notesnook.ids.dialogs.vault.changePwd).typeText("2362");
|
||||
await tapByText("Change");
|
||||
await sleep(2000);
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await openLockedNote("2362");
|
||||
|
||||
139
apps/mobile/share/add-notebooks.jsx
Normal file
139
apps/mobile/share/add-notebooks.jsx
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useDBItem } from "../app/hooks/use-db-item";
|
||||
import { useShareStore } from "./store";
|
||||
|
||||
export const AddNotebooks = ({ onPress }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const notebookIds = useShareStore((state) => state.selectedNotebooks);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.background,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10,
|
||||
paddingVertical: 10,
|
||||
paddingTop: 5
|
||||
}}
|
||||
>
|
||||
{!notebookIds || !notebookIds.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row",
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="notebook-outline"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add to notebook
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{notebookIds.map((id) => (
|
||||
<NotebookItem key={id} id={id} />
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.primary.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-more"
|
||||
>
|
||||
<Icon name="plus" size={16} /> Add more
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const NotebookItem = ({ id }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(id, "notebook");
|
||||
const notebookIds = useShareStore((state) => state.selectedNotebooks);
|
||||
|
||||
return !item ? null : (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = notebookIds.indexOf(id);
|
||||
const selectedNotebooks = [...notebookIds];
|
||||
selectedNotebooks.splice(index, 1);
|
||||
useShareStore.getState().setSelectedNotebooks(selectedNotebooks);
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<Icon name={"notebook-outline"} size={15} /> {item.title}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
133
apps/mobile/share/add-tags.jsx
Normal file
133
apps/mobile/share/add-tags.jsx
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import React from "react";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useDBItem } from "../app/hooks/use-db-item";
|
||||
import { useShareStore } from "./store";
|
||||
|
||||
export const AddTags = ({ onPress }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const tagIds = useShareStore((state) => state.selectedTags);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
minHeight: 45,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.background,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
{!tagIds || !tagIds.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="pound"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add tags
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{tagIds.map((id) => (
|
||||
<TagItem key={id} tagId={id} />
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.primary.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-tag"
|
||||
>
|
||||
<Icon name="plus" size={17} />
|
||||
Add tag
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const TagItem = ({ tagId }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(tagId, "tag");
|
||||
const tagIds = useShareStore((state) => state.selectedTags);
|
||||
return !item ? null : (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = tagIds.indexOf(tagId);
|
||||
const selectedTags = [...tagId];
|
||||
selectedTags.splice(index, 1);
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}}
|
||||
>
|
||||
#{item.title}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -1,391 +0,0 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Platform,
|
||||
StatusBar,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions,
|
||||
FlatList
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../app/common/database";
|
||||
import { getElevationStyle } from "../app/utils/elevation";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
|
||||
const ListItem = ({ item, mode, close }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const selectedNotebooks = useShareStore((state) => state.selectedNotebooks);
|
||||
const selectedTags = useShareStore((state) => state.selectedTags);
|
||||
const isSelected =
|
||||
mode === "appendNote"
|
||||
? false
|
||||
: mode === "selectTags"
|
||||
? selectedTags.indexOf(item.alias) > -1
|
||||
: selectedNotebooks.findIndex(
|
||||
(selected) => selected.id === item.id && selected.type === item.type
|
||||
) > -1;
|
||||
|
||||
const SearchSetters = React.useMemo(
|
||||
() => ({
|
||||
appendNote: (item) => {
|
||||
useShareStore.getState().setAppendNote(item);
|
||||
close();
|
||||
},
|
||||
selectNotebooks: (item) => {
|
||||
const selectedNotebooks = [
|
||||
...useShareStore.getState().selectedNotebooks
|
||||
];
|
||||
const currentIndex = selectedNotebooks.findIndex(
|
||||
(selected) => selected.id === item.id && selected.type === item.type
|
||||
);
|
||||
if (currentIndex === -1) {
|
||||
selectedNotebooks.push(item);
|
||||
} else {
|
||||
selectedNotebooks.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedNotebooks(selectedNotebooks);
|
||||
},
|
||||
selectTags: (item) => {
|
||||
const selectedTags = [...useShareStore.getState().selectedTags];
|
||||
const currentIndex = selectedTags.indexOf(item.alias);
|
||||
if (currentIndex === -1) {
|
||||
selectedTags.push(item.alias);
|
||||
} else {
|
||||
selectedTags.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}
|
||||
}),
|
||||
[close]
|
||||
);
|
||||
const set = React.useMemo(() => SearchSetters[mode], [mode, SearchSetters]);
|
||||
|
||||
const onSelectItem = async (item) => {
|
||||
if (item.locked) {
|
||||
return;
|
||||
}
|
||||
set(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onSelectItem(item)}
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
borderBottomWidth: item.topics?.length > 0 ? 0 : 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
justifyContent: "center",
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
paddingLeft: item.type === "topic" ? 0 : 12
|
||||
}}
|
||||
>
|
||||
{item.type !== "note" ? (
|
||||
<Icon
|
||||
name={
|
||||
!isSelected
|
||||
? "checkbox-blank-circle-outline"
|
||||
: "check-circle-outline"
|
||||
}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={20}
|
||||
color={isSelected ? colors.primary.accent : colors.secondary.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.primary.paragraph,
|
||||
fontWeight: item.type === "topic" ? "normal" : "bold",
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
{item.type === "tag" ? "#" : ""}
|
||||
{item.alias || item.title}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{item.type === "notebook" && item.topics && item.topics.length > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: 40,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{item.topics.map((topic) => (
|
||||
<ListItem key={topic.id} item={topic} mode={mode} close={close} />
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const SearchGetters = {
|
||||
appendNote: () =>
|
||||
groupArray([...db.notes.all], db.settings.getGroupOptions("notes")).filter(
|
||||
(item) => item.type === "note"
|
||||
),
|
||||
selectNotebooks: () =>
|
||||
groupArray(
|
||||
[...db.notebooks.all],
|
||||
db.settings.getGroupOptions("notebooks")
|
||||
).filter((item) => item.type === "notebook"),
|
||||
selectTags: () => {
|
||||
const selected = useShareStore.getState().selectedTags;
|
||||
const tags = [];
|
||||
if (selected)
|
||||
tags.push(
|
||||
...selected.map((t) => ({
|
||||
alias: t,
|
||||
type: "tag"
|
||||
}))
|
||||
);
|
||||
|
||||
for (let tag of db.tags.all) {
|
||||
const index = tags.findIndex((t) => t.alias === tag.alias);
|
||||
// Skip selected tags as they are already in the list.
|
||||
if (index > -1) continue;
|
||||
tags.push(tag);
|
||||
}
|
||||
return groupArray([...tags], {
|
||||
groupBy: "none",
|
||||
sortDirection: "asc",
|
||||
sortBy: "title"
|
||||
}).filter((item) => item.type === "tag");
|
||||
}
|
||||
};
|
||||
|
||||
const SearchLookup = {
|
||||
appendNote: (items, kwd) => {
|
||||
return db.lookup.notes(items, kwd);
|
||||
},
|
||||
selectNotebooks: (items, kwd) => {
|
||||
return db.lookup.notebooks(items, kwd);
|
||||
},
|
||||
selectTags: (items, kwd) => {
|
||||
return db.lookup.tags(items, kwd);
|
||||
}
|
||||
};
|
||||
|
||||
const SearchPlaceholder = {
|
||||
appendNote: "Search for a note",
|
||||
selectNotebooks: "Search for a notebook",
|
||||
selectTags: "Search for a tag"
|
||||
};
|
||||
|
||||
export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
const { height } = useWindowDimensions();
|
||||
const timer = useRef(null);
|
||||
const inputRef = useRef();
|
||||
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
const searchableItems = useRef(null);
|
||||
const searchKeyword = useRef(null);
|
||||
|
||||
const insets =
|
||||
Platform.OS === "android"
|
||||
? { top: StatusBar.currentHeight }
|
||||
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSafeAreaInsets();
|
||||
|
||||
const get = SearchGetters[mode];
|
||||
const lookup = SearchLookup[mode];
|
||||
|
||||
const onSearch = async () => {
|
||||
if (!searchableItems.current) {
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
}
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = null;
|
||||
}
|
||||
timer.current = setTimeout(async () => {
|
||||
if (!searchKeyword.current) {
|
||||
setSearchResults([]);
|
||||
setSearchResults(get());
|
||||
return;
|
||||
}
|
||||
setSearchResults(
|
||||
await lookup(searchableItems.current, searchKeyword.current)
|
||||
);
|
||||
}, 150);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
setSearchResults(searchableItems.current);
|
||||
})();
|
||||
}, [get]);
|
||||
|
||||
const renderItem = ({ item }) =>
|
||||
!item.locked ? <ListItem item={item} mode={mode} close={close} /> : null;
|
||||
|
||||
let extra = quicknote
|
||||
? {
|
||||
marginTop: -insets.top,
|
||||
paddingTop: insets.top
|
||||
}
|
||||
: {};
|
||||
const searchHeight = height - getKeyboardHeight();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: Platform.OS === "android" ? 20 : 0,
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: quicknote ? 0 : 10,
|
||||
width: quicknote ? "100%" : "95%",
|
||||
minHeight: 250,
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
zIndex: 999,
|
||||
...getElevationStyle(quicknote ? 1 : 5),
|
||||
...extra
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
marginBottom: 10,
|
||||
height: 50
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="arrow-left"
|
||||
color={colors.secondary.icon}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={25}
|
||||
onPress={close}
|
||||
/>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
placeholder={SearchPlaceholder[mode]}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
style={{
|
||||
fontSize: 15,
|
||||
flex: 1
|
||||
}}
|
||||
onChangeText={(value) => {
|
||||
if (mode === "selectTags") {
|
||||
searchKeyword.current = db.tags.sanitize(value);
|
||||
} else {
|
||||
searchKeyword.current = value;
|
||||
}
|
||||
onSearch();
|
||||
}}
|
||||
onSubmitEditing={onSearch}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
name="magnify"
|
||||
color={colors.primary.icon}
|
||||
size={25}
|
||||
onPress={onSearch}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
maxHeight: searchHeight > 550 ? 550 : searchHeight,
|
||||
height: searchHeight > 550 ? 550 : searchHeight
|
||||
}}
|
||||
>
|
||||
<FlatList
|
||||
data={searchResults}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
renderItem={renderItem}
|
||||
ListHeaderComponent={
|
||||
mode === "selectTags" &&
|
||||
searchKeyword.current &&
|
||||
searchKeyword.current.length > 0 &&
|
||||
!searchResults.find(
|
||||
(item) => item.title === searchKeyword.current
|
||||
) ? (
|
||||
<ListItem
|
||||
item={{
|
||||
type: "tag",
|
||||
alias: searchKeyword.current
|
||||
}}
|
||||
mode={mode}
|
||||
close={close}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon
|
||||
}}
|
||||
>
|
||||
{searchKeyword.current
|
||||
? `No results found for "${searchKeyword.current}"`
|
||||
: "Search for a note to append to it."}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
548
apps/mobile/share/search.tsx
Normal file
548
apps/mobile/share/search.tsx
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Item, Note, Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FlatList,
|
||||
Platform,
|
||||
StatusBar,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../app/common/database";
|
||||
import { useDBItem } from "../app/hooks/use-db-item";
|
||||
import { useNotebook } from "../app/hooks/use-notebook";
|
||||
import { getElevationStyle } from "../app/utils/elevation";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
import { createItemSelectionStore } from "../app/stores/item-selection-store";
|
||||
import create from "zustand";
|
||||
|
||||
export const useNotebookExpandedStore = create<{
|
||||
expanded: {
|
||||
[id: string]: boolean;
|
||||
};
|
||||
setExpanded: (id: string) => void;
|
||||
}>((set, get) => ({
|
||||
expanded: {},
|
||||
setExpanded(id: string) {
|
||||
set({
|
||||
expanded: {
|
||||
...get().expanded,
|
||||
[id]: !get().expanded[id]
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
const SearchSetters = {
|
||||
appendNote: (id: string) => {
|
||||
useShareStore.getState().setAppendNote(id);
|
||||
close();
|
||||
},
|
||||
selectNotebooks: (id: string) => {
|
||||
const selectedNotebooks = [...useShareStore.getState().selectedNotebooks];
|
||||
const currentIndex = selectedNotebooks.findIndex(
|
||||
(selected) => id === selected
|
||||
);
|
||||
if (currentIndex === -1) {
|
||||
selectedNotebooks.push(id as never);
|
||||
} else {
|
||||
selectedNotebooks.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedNotebooks(selectedNotebooks);
|
||||
},
|
||||
selectTags: (id: string) => {
|
||||
const selectedTags = [...useShareStore.getState().selectedTags];
|
||||
const currentIndex = selectedTags.indexOf(id as never);
|
||||
if (currentIndex === -1) {
|
||||
selectedTags.push(id as never);
|
||||
} else {
|
||||
selectedTags.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}
|
||||
};
|
||||
|
||||
const SearchGetters = {
|
||||
appendNote: () => db.notes.all.sorted(db.settings.getGroupOptions("notes")),
|
||||
selectNotebooks: () =>
|
||||
db.notebooks.roots.sorted(db.settings.getGroupOptions("notebooks")),
|
||||
selectTags: () => {
|
||||
return db.tags.all.sorted(db.settings.getGroupOptions("tags"));
|
||||
}
|
||||
};
|
||||
|
||||
const SearchLookup = {
|
||||
appendNote: (query: string) => {
|
||||
return db.lookup.notes(query).sorted();
|
||||
},
|
||||
selectNotebooks: (query: string) => {
|
||||
return db.lookup.notebooks(query).sorted();
|
||||
},
|
||||
selectTags: (query: string) => {
|
||||
return db.lookup.tags(query).sorted();
|
||||
}
|
||||
};
|
||||
|
||||
const SearchPlaceholder = {
|
||||
appendNote: "Search for a note",
|
||||
selectNotebooks: "Search for a notebook",
|
||||
selectTags: "Search for a tag"
|
||||
};
|
||||
|
||||
type SearchMode = "appendNote" | "selectNotebooks" | "selectTags";
|
||||
|
||||
const NotebookItem = ({
|
||||
id,
|
||||
mode,
|
||||
close,
|
||||
items,
|
||||
level = 0
|
||||
}: {
|
||||
id: string;
|
||||
mode: SearchMode;
|
||||
close?: () => void;
|
||||
level?: number;
|
||||
items?: VirtualizedGrouping<Notebook>;
|
||||
}) => {
|
||||
const isExpanded = useNotebookExpandedStore((state) => state.expanded[id]);
|
||||
const { nestedNotebooks, notebook } = useNotebook(id, items);
|
||||
const { colors } = useThemeColors();
|
||||
const isSelected = useShareStore(
|
||||
(state) =>
|
||||
state.selectedNotebooks.findIndex((selectedId) => id === selectedId) > -1
|
||||
);
|
||||
const set = SearchSetters[mode];
|
||||
|
||||
const onSelectItem = async () => {
|
||||
set(id);
|
||||
};
|
||||
|
||||
return !notebook ? (
|
||||
<View
|
||||
style={{
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onSelectItem()}
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
height: 40,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{nestedNotebooks?.ids.length ? (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
onPress={() => {
|
||||
useNotebookExpandedStore.getState().setExpanded(id);
|
||||
}}
|
||||
activeOpacity={1}
|
||||
>
|
||||
<Icon
|
||||
size={20}
|
||||
color={colors.primary.icon}
|
||||
name={isExpanded ? "chevron-down" : "chevron-right"}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: 35,
|
||||
height: 30,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Icon
|
||||
name={
|
||||
!isSelected
|
||||
? "checkbox-blank-circle-outline"
|
||||
: "check-circle-outline"
|
||||
}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={20}
|
||||
color={isSelected ? colors.primary.accent : colors.secondary.icon}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.primary.paragraph,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
{notebook.title}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{nestedNotebooks?.ids?.length && isExpanded ? (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: level + 1 > 0 && level + 1 < 5 ? 15 : 0,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{(nestedNotebooks.ids as string[]).map((id) => (
|
||||
<NotebookItem
|
||||
key={id}
|
||||
id={id}
|
||||
mode={mode}
|
||||
close={close}
|
||||
level={level + 1}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const ListItem = ({
|
||||
id,
|
||||
mode,
|
||||
close,
|
||||
items
|
||||
}: {
|
||||
id: string;
|
||||
mode: SearchMode;
|
||||
close?: () => void;
|
||||
items?: VirtualizedGrouping<Item>;
|
||||
}) => {
|
||||
const [item] = useDBItem(
|
||||
id,
|
||||
mode === "appendNote" ? "note" : "tag",
|
||||
items as any
|
||||
);
|
||||
const { colors } = useThemeColors();
|
||||
const isSelected = useShareStore((state) =>
|
||||
mode === "appendNote" ? false : state.selectedTags.indexOf(id as never) > -1
|
||||
);
|
||||
|
||||
const set = SearchSetters[mode];
|
||||
|
||||
const onSelectItem = async () => {
|
||||
if ((item as Note)?.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(id);
|
||||
};
|
||||
|
||||
return !item ? null : (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onSelectItem()}
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
justifyContent: "center",
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
{item.type !== "note" ? (
|
||||
<Icon
|
||||
name={
|
||||
!isSelected
|
||||
? "checkbox-blank-circle-outline"
|
||||
: "check-circle-outline"
|
||||
}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={20}
|
||||
color={isSelected ? colors.primary.accent : colors.secondary.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.primary.paragraph,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
{item.type === "tag" ? "#" : ""}
|
||||
{item.title}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export const Search = ({
|
||||
close,
|
||||
getKeyboardHeight,
|
||||
quicknote,
|
||||
mode
|
||||
}: {
|
||||
close?: () => void;
|
||||
getKeyboardHeight: () => number;
|
||||
quicknote?: boolean;
|
||||
mode: SearchMode;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const { height } = useWindowDimensions();
|
||||
const timer = useRef<NodeJS.Timeout>();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const [items, setItems] = useState<VirtualizedGrouping<Item>>();
|
||||
const [searchKeyword, setSearchKeyword] = useState<string>();
|
||||
const [queryExists, setQueryExists] = useState(false);
|
||||
|
||||
const checkQueryExists = (query: string) => {
|
||||
if (!query) {
|
||||
setQueryExists(false);
|
||||
}
|
||||
db.tags.all
|
||||
.find((v) => v.and([v(`title`, "==", query)]))
|
||||
.then((exists) => setQueryExists(!!exists));
|
||||
};
|
||||
|
||||
const insets =
|
||||
Platform.OS === "android"
|
||||
? { top: StatusBar.currentHeight }
|
||||
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSafeAreaInsets();
|
||||
|
||||
const get = SearchGetters[mode];
|
||||
const lookup = SearchLookup[mode];
|
||||
|
||||
const onSearch = async () => {
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = undefined;
|
||||
}
|
||||
timer.current = setTimeout(async () => {
|
||||
if (!searchKeyword) {
|
||||
setItems(undefined);
|
||||
setItems(await get());
|
||||
setQueryExists(false);
|
||||
return;
|
||||
}
|
||||
setItems(await lookup(searchKeyword));
|
||||
}, 150);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await initDatabase();
|
||||
setItems(await get());
|
||||
})();
|
||||
}, [get]);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ item }: { item: string }) =>
|
||||
mode === "selectNotebooks" ? (
|
||||
<NotebookItem
|
||||
id={item}
|
||||
mode={mode}
|
||||
close={close}
|
||||
items={items as any}
|
||||
/>
|
||||
) : (
|
||||
<ListItem id={item} mode={mode} close={close} items={items} />
|
||||
),
|
||||
[close, mode, items]
|
||||
);
|
||||
|
||||
const customStyles = quicknote
|
||||
? {
|
||||
marginTop: -(insets.top || 0),
|
||||
paddingTop: insets.top
|
||||
}
|
||||
: {};
|
||||
const searchHeight = height - getKeyboardHeight();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: Platform.OS === "android" ? 20 : 0,
|
||||
backgroundColor: colors.primary.background,
|
||||
borderRadius: quicknote ? 0 : 10,
|
||||
width: quicknote ? "100%" : "95%",
|
||||
minHeight: 250,
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
zIndex: 999,
|
||||
...getElevationStyle(quicknote ? 1 : 5),
|
||||
...customStyles
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: 6,
|
||||
marginBottom: 10,
|
||||
height: 50
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="arrow-left"
|
||||
color={colors.secondary.icon}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={25}
|
||||
onPress={close}
|
||||
/>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
placeholder={SearchPlaceholder[mode]}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
style={{
|
||||
fontSize: 16,
|
||||
flex: 1
|
||||
}}
|
||||
onChangeText={(value) => {
|
||||
setSearchKeyword(value);
|
||||
onSearch();
|
||||
checkQueryExists(value);
|
||||
}}
|
||||
onSubmitEditing={onSearch}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
name="magnify"
|
||||
color={colors.primary.icon}
|
||||
size={25}
|
||||
onPress={onSearch}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
maxHeight: searchHeight > 550 ? 550 : searchHeight,
|
||||
height: searchHeight > 550 ? 550 : searchHeight
|
||||
}}
|
||||
>
|
||||
{mode === "selectTags" && !queryExists && searchKeyword ? (
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
height: 40,
|
||||
alignItems: "center"
|
||||
}}
|
||||
activeOpacity={1}
|
||||
onPress={async () => {
|
||||
if (!searchKeyword) return;
|
||||
const tagId = await db.tags.add({
|
||||
title: searchKeyword
|
||||
});
|
||||
SearchSetters.selectTags(tagId);
|
||||
onSearch();
|
||||
checkQueryExists(searchKeyword);
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.primary.paragraph,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add #{searchKeyword}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
|
||||
<FlatList
|
||||
data={items?.ids as string[]}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
renderItem={renderItem}
|
||||
windowSize={1}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon
|
||||
}}
|
||||
>
|
||||
{searchKeyword
|
||||
? `No results found for "${searchKeyword}"`
|
||||
: mode === "appendNote"
|
||||
? "No notes"
|
||||
: mode === "selectTags"
|
||||
? "No tags"
|
||||
: "No notebooks"}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -52,6 +52,8 @@ import { FILE_SIZE_LIMIT, IMAGE_SIZE_LIMIT } from "../app/utils/constants";
|
||||
import { getElevationStyle } from "../app/utils/elevation";
|
||||
import { eOnLoadNote } from "../app/utils/events";
|
||||
import { NoteBundle } from "../app/utils/note-bundle";
|
||||
import { AddNotebooks } from "./add-notebooks";
|
||||
import { AddTags } from "./add-tags";
|
||||
import { Editor } from "./editor";
|
||||
import { HtmlLoadingWebViewAgent, fetchHandle } from "./fetch-webview";
|
||||
import { Search } from "./search";
|
||||
@@ -281,8 +283,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
};
|
||||
} else {
|
||||
_note = { ...note };
|
||||
_note.tags = useShareStore.getState().selectedTags || [];
|
||||
|
||||
_note.content.data = noteContent.current;
|
||||
_note.sessionId = Date.now();
|
||||
}
|
||||
@@ -290,7 +290,8 @@ const ShareView = ({ quicknote = false }) => {
|
||||
await NoteBundle.createNotes({
|
||||
files: rawFiles,
|
||||
note: _note,
|
||||
notebooks: useShareStore.getState().selectedNotebooks
|
||||
notebooks: useShareStore.getState().selectedNotebooks,
|
||||
tags: useShareStore.getState().selectedTags
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -840,217 +841,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const AddNotebooks = ({ onPress }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const notebooks = useShareStore((state) => state.selectedNotebooks);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.background,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10,
|
||||
paddingVertical: 10,
|
||||
paddingTop: 5
|
||||
}}
|
||||
>
|
||||
{!notebooks || !notebooks.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row",
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="notebook-outline"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add to notebook
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{notebooks.map((item) => (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = notebooks.findIndex(
|
||||
(nb) => nb.id === item.id && nb.type === item.type
|
||||
);
|
||||
const selectedNotebooks = [...notebooks];
|
||||
selectedNotebooks.splice(index, 1);
|
||||
useShareStore
|
||||
.getState()
|
||||
.setSelectedNotebooks(selectedNotebooks);
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<Icon
|
||||
name={item.type === "topic" ? "bookmark" : "notebook-outline"}
|
||||
size={15}
|
||||
/>{" "}
|
||||
{item.title}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.primary.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-more"
|
||||
>
|
||||
<Icon name="plus" size={16} /> Add more
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const AddTags = ({ onPress }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const tags = useShareStore((state) => state.selectedTags);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
minHeight: 45,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.background,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
{!tags || !tags.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="pound"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add tags
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.secondary.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = tags.indexOf(tag);
|
||||
const selectedTags = [...tags];
|
||||
selectedTags.splice(index, 1);
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}}
|
||||
key={tag}
|
||||
>
|
||||
#{tag}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.primary.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-tag"
|
||||
>
|
||||
<Icon name="plus" size={17} />
|
||||
Add tag
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const Button = ({
|
||||
title,
|
||||
onPress,
|
||||
|
||||
Reference in New Issue
Block a user