mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
mobile: top level notes
This commit is contained in:
committed by
Abdullah Atta
parent
765fcafc38
commit
a6021943e1
@@ -17,70 +17,34 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { FlatList } from "react-native-gesture-handler";
|
import { FlatList } from "react-native-gesture-handler";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../common/database";
|
import { db } from "../../common/database";
|
||||||
import filesystem from "../../common/filesystem";
|
import filesystem from "../../common/filesystem";
|
||||||
import {
|
import { presentSheet } from "../../services/event-manager";
|
||||||
eSubscribeEvent,
|
|
||||||
eUnSubscribeEvent
|
|
||||||
} from "../../services/event-manager";
|
|
||||||
import { useThemeStore } from "../../stores/use-theme-store";
|
import { useThemeStore } from "../../stores/use-theme-store";
|
||||||
import {
|
|
||||||
eCloseAttachmentDialog,
|
|
||||||
eOpenAttachmentsDialog
|
|
||||||
} from "../../utils/events";
|
|
||||||
import { SIZE } from "../../utils/size";
|
import { SIZE } from "../../utils/size";
|
||||||
import DialogHeader from "../dialog/dialog-header";
|
import DialogHeader from "../dialog/dialog-header";
|
||||||
import { Toast } from "../toast";
|
|
||||||
import Input from "../ui/input";
|
import Input from "../ui/input";
|
||||||
import Seperator from "../ui/seperator";
|
import Seperator from "../ui/seperator";
|
||||||
import SheetWrapper from "../ui/sheet";
|
|
||||||
import Paragraph from "../ui/typography/paragraph";
|
import Paragraph from "../ui/typography/paragraph";
|
||||||
import { AttachmentItem } from "./attachment-item";
|
import { AttachmentItem } from "./attachment-item";
|
||||||
export const AttachmentDialog = () => {
|
|
||||||
|
export const AttachmentDialog = ({ data }) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [visible, setVisible] = useState(false);
|
const [note, setNote] = useState(data);
|
||||||
const [note, setNote] = useState(null);
|
|
||||||
const actionSheetRef = useRef();
|
const actionSheetRef = useRef();
|
||||||
const [attachments, setAttachments] = useState([]);
|
const [attachments, setAttachments] = useState(
|
||||||
|
data
|
||||||
|
? db.attachments.ofNote(data.id, "all")
|
||||||
|
: [...(db.attachments.all || [])]
|
||||||
|
);
|
||||||
const attachmentSearchValue = useRef();
|
const attachmentSearchValue = useRef();
|
||||||
const searchTimer = useRef();
|
const searchTimer = useRef();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
eSubscribeEvent(eOpenAttachmentsDialog, open);
|
|
||||||
eSubscribeEvent(eCloseAttachmentDialog, close);
|
|
||||||
return () => {
|
|
||||||
eUnSubscribeEvent(eOpenAttachmentsDialog, open);
|
|
||||||
eUnSubscribeEvent(eCloseAttachmentDialog, close);
|
|
||||||
};
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
const open = (data) => {
|
|
||||||
if (data?.id) {
|
|
||||||
setNote(data);
|
|
||||||
let _attachments = db.attachments.ofNote(data.id, "all");
|
|
||||||
setAttachments(_attachments);
|
|
||||||
} else {
|
|
||||||
setAttachments([...db.attachments.all]);
|
|
||||||
}
|
|
||||||
setVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
actionSheetRef.current?.show();
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
actionSheetRef.current?.hide();
|
|
||||||
setVisible(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onChangeText = (text) => {
|
const onChangeText = (text) => {
|
||||||
attachmentSearchValue.current = text;
|
attachmentSearchValue.current = text;
|
||||||
if (
|
if (
|
||||||
@@ -104,107 +68,104 @@ export const AttachmentDialog = () => {
|
|||||||
<AttachmentItem setAttachments={setAttachments} attachment={item} />
|
<AttachmentItem setAttachments={setAttachments} attachment={item} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return !visible ? null : (
|
return (
|
||||||
<SheetWrapper
|
<View
|
||||||
centered={false}
|
style={{
|
||||||
fwdRef={actionSheetRef}
|
width: "100%",
|
||||||
onClose={async () => {
|
alignSelf: "center",
|
||||||
setVisible(false);
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toast context="local" />
|
<DialogHeader
|
||||||
<View
|
title={note ? "Attachments" : "Manage attachments"}
|
||||||
style={{
|
paragraph="Tap on an attachment to view properties"
|
||||||
width: "100%",
|
button={{
|
||||||
alignSelf: "center",
|
title: "Check all",
|
||||||
paddingHorizontal: 12
|
type: "grayAccent",
|
||||||
}}
|
loading: loading,
|
||||||
>
|
onPress: async () => {
|
||||||
<DialogHeader
|
setLoading(true);
|
||||||
title={note ? "Attachments" : "Manage attachments"}
|
for (let attachment of attachments) {
|
||||||
paragraph="Tap on an attachment to view properties"
|
let result = await filesystem.checkAttachment(
|
||||||
button={{
|
attachment.metadata.hash
|
||||||
title: "Check all",
|
);
|
||||||
type: "grayAccent",
|
if (result.failed) {
|
||||||
loading: loading,
|
db.attachments.markAsFailed(
|
||||||
onPress: async () => {
|
attachment.metadata.hash,
|
||||||
setLoading(true);
|
result.failed
|
||||||
for (let attachment of attachments) {
|
|
||||||
let result = await filesystem.checkAttachment(
|
|
||||||
attachment.metadata.hash
|
|
||||||
);
|
);
|
||||||
if (result.failed) {
|
} else {
|
||||||
db.attachments.markAsFailed(
|
db.attachments.markAsFailed(attachment.id, null);
|
||||||
attachment.metadata.hash,
|
|
||||||
result.failed
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
db.attachments.markAsFailed(attachment.id, null);
|
|
||||||
}
|
|
||||||
setAttachments([...db.attachments.all]);
|
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setAttachments([...db.attachments.all]);
|
||||||
}
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Seperator />
|
||||||
|
{!note ? (
|
||||||
|
<Input
|
||||||
|
placeholder="Filter attachments by filename, type or hash"
|
||||||
|
onChangeText={onChangeText}
|
||||||
|
onSubmit={() => {
|
||||||
|
onChangeText(attachmentSearchValue.current);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Seperator />
|
) : null}
|
||||||
{!note ? (
|
|
||||||
<Input
|
<FlatList
|
||||||
placeholder="Filter attachments by filename, type or hash"
|
nestedScrollEnabled
|
||||||
onChangeText={onChangeText}
|
overScrollMode="never"
|
||||||
onSubmit={() => {
|
scrollToOverflowEnabled={false}
|
||||||
onChangeText(attachmentSearchValue.current);
|
keyboardDismissMode="none"
|
||||||
|
keyboardShouldPersistTaps="always"
|
||||||
|
onMomentumScrollEnd={() => {
|
||||||
|
actionSheetRef.current?.handleChildScrollEnd();
|
||||||
|
}}
|
||||||
|
ListEmptyComponent={
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
height: 150,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="attachment" size={60} color={colors.icon} />
|
||||||
|
<Paragraph>
|
||||||
|
{note ? "No attachments on this note" : "No attachments"}
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
ListFooterComponent={
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
height: 350
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
}
|
||||||
|
data={attachments}
|
||||||
|
keyExtractor={(item) => item.id}
|
||||||
|
renderItem={renderItem}
|
||||||
|
/>
|
||||||
|
|
||||||
<FlatList
|
<Paragraph
|
||||||
nestedScrollEnabled
|
color={colors.icon}
|
||||||
overScrollMode="never"
|
size={SIZE.xs}
|
||||||
scrollToOverflowEnabled={false}
|
style={{
|
||||||
keyboardDismissMode="none"
|
textAlign: "center",
|
||||||
keyboardShouldPersistTaps="always"
|
marginTop: 10
|
||||||
onMomentumScrollEnd={() => {
|
}}
|
||||||
actionSheetRef.current?.handleChildScrollEnd();
|
>
|
||||||
}}
|
<Icon name="shield-key-outline" size={SIZE.xs} color={colors.icon} />
|
||||||
ListEmptyComponent={
|
{" "}All attachments are end-to-end encrypted.
|
||||||
<View
|
</Paragraph>
|
||||||
style={{
|
</View>
|
||||||
height: 150,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name="attachment" size={60} color={colors.icon} />
|
|
||||||
<Paragraph>
|
|
||||||
{note ? "No attachments on this note" : "No attachments"}
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
ListFooterComponent={
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
height: 350
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
data={attachments}
|
|
||||||
keyExtractor={(item) => item.id}
|
|
||||||
renderItem={renderItem}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Paragraph
|
|
||||||
color={colors.icon}
|
|
||||||
size={SIZE.xs}
|
|
||||||
style={{
|
|
||||||
textAlign: "center",
|
|
||||||
marginTop: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name="shield-key-outline" size={SIZE.xs} color={colors.icon} />
|
|
||||||
{" "}All attachments are end-to-end encrypted.
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
</SheetWrapper>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AttachmentDialog.present = (note) => {
|
||||||
|
presentSheet({
|
||||||
|
component: () => <AttachmentDialog data={note} />
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import React from "react";
|
|||||||
import { useNoteStore } from "../../stores/use-notes-store";
|
import { useNoteStore } from "../../stores/use-notes-store";
|
||||||
import { useThemeStore } from "../../stores/use-theme-store";
|
import { useThemeStore } from "../../stores/use-theme-store";
|
||||||
import { AnnouncementDialog } from "../announcements";
|
import { AnnouncementDialog } from "../announcements";
|
||||||
import { AttachmentDialog } from "../attachments";
|
|
||||||
import AuthModal from "../auth/auth-modal";
|
import AuthModal from "../auth/auth-modal";
|
||||||
import { SessionExpired } from "../auth/session-expired";
|
import { SessionExpired } from "../auth/session-expired";
|
||||||
import { Dialog } from "../dialog";
|
import { Dialog } from "../dialog";
|
||||||
@@ -34,10 +33,6 @@ import MergeConflicts from "../merge-conflicts";
|
|||||||
import PremiumDialog from "../premium";
|
import PremiumDialog from "../premium";
|
||||||
import { Expiring } from "../premium/expiring";
|
import { Expiring } from "../premium/expiring";
|
||||||
import SheetProvider from "../sheet-provider";
|
import SheetProvider from "../sheet-provider";
|
||||||
import { AddNotebookSheet } from "../sheets/add-notebook";
|
|
||||||
import AddToNotebookSheet from "../sheets/add-to";
|
|
||||||
import ManageTagsSheet from "../sheets/manage-tags";
|
|
||||||
import PublishNoteSheet from "../sheets/publish-note";
|
|
||||||
import RateAppSheet from "../sheets/rate-app";
|
import RateAppSheet from "../sheets/rate-app";
|
||||||
import RecoveryKeySheet from "../sheets/recovery-key";
|
import RecoveryKeySheet from "../sheets/recovery-key";
|
||||||
import RestoreDataSheet from "../sheets/restore-data";
|
import RestoreDataSheet from "../sheets/restore-data";
|
||||||
@@ -51,7 +46,6 @@ const DialogProvider = () => {
|
|||||||
<LoadingDialog />
|
<LoadingDialog />
|
||||||
<Dialog context="global" />
|
<Dialog context="global" />
|
||||||
<AddTopicDialog colors={colors} />
|
<AddTopicDialog colors={colors} />
|
||||||
<AddNotebookSheet colors={colors} />
|
|
||||||
<PremiumDialog colors={colors} />
|
<PremiumDialog colors={colors} />
|
||||||
<AuthModal colors={colors} />
|
<AuthModal colors={colors} />
|
||||||
<MergeConflicts />
|
<MergeConflicts />
|
||||||
@@ -61,12 +55,8 @@ const DialogProvider = () => {
|
|||||||
<RestoreDataSheet />
|
<RestoreDataSheet />
|
||||||
<ResultDialog />
|
<ResultDialog />
|
||||||
<VaultDialog colors={colors} />
|
<VaultDialog colors={colors} />
|
||||||
<AddToNotebookSheet colors={colors} />
|
|
||||||
<RateAppSheet />
|
<RateAppSheet />
|
||||||
<ImagePreview />
|
<ImagePreview />
|
||||||
<PublishNoteSheet />
|
|
||||||
<ManageTagsSheet />
|
|
||||||
<AttachmentDialog />
|
|
||||||
{loading ? null : <Expiring />}
|
{loading ? null : <Expiring />}
|
||||||
<AnnouncementDialog />
|
<AnnouncementDialog />
|
||||||
<SessionExpired />
|
<SessionExpired />
|
||||||
|
|||||||
@@ -59,14 +59,12 @@ export class AddTopicDialog extends React.Component {
|
|||||||
|
|
||||||
addNewTopic = async () => {
|
addNewTopic = async () => {
|
||||||
try {
|
try {
|
||||||
this.setState({ loading: true });
|
|
||||||
if (!this.title || this.title?.trim() === "") {
|
if (!this.title || this.title?.trim() === "") {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
heading: "Topic title is required",
|
heading: "Topic title is required",
|
||||||
type: "error",
|
type: "error",
|
||||||
context: "local"
|
context: "local"
|
||||||
});
|
});
|
||||||
this.setState({ loading: false });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +76,11 @@ export class AddTopicDialog extends React.Component {
|
|||||||
|
|
||||||
await db.notebooks.notebook(topic.notebookId).topics.add(topic);
|
await db.notebooks.notebook(topic.notebookId).topics.add(topic);
|
||||||
}
|
}
|
||||||
this.setState({ loading: false });
|
|
||||||
this.close();
|
this.close();
|
||||||
Navigation.queueRoutesForUpdate("Notebooks", "Notebook", "TopicNotes");
|
setTimeout(() => {
|
||||||
useMenuStore.getState().setMenuPins();
|
Navigation.queueRoutesForUpdate("Notebooks", "Notebook", "TopicNotes");
|
||||||
|
useMenuStore.getState().setMenuPins();
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -177,7 +176,6 @@ export class AddTopicDialog extends React.Component {
|
|||||||
positiveTitle={this.toEdit ? "Save" : "Add"}
|
positiveTitle={this.toEdit ? "Save" : "Add"}
|
||||||
onPressNegative={() => this.close()}
|
onPressNegative={() => this.close()}
|
||||||
onPressPositive={() => this.addNewTopic()}
|
onPressPositive={() => this.addNewTopic()}
|
||||||
loading={this.state.loading}
|
|
||||||
/>
|
/>
|
||||||
</DialogContainer>
|
</DialogContainer>
|
||||||
<Toast context="local" />
|
<Toast context="local" />
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import { useThemeStore } from "../../stores/use-theme-store";
|
|||||||
import { eScrollEvent } from "../../utils/events";
|
import { eScrollEvent } from "../../utils/events";
|
||||||
import { SIZE } from "../../utils/size";
|
import { SIZE } from "../../utils/size";
|
||||||
import Heading from "../ui/typography/heading";
|
import Heading from "../ui/typography/heading";
|
||||||
import Paragraph from "../ui/typography/paragraph";
|
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import Tag from "../ui/tag";
|
import Tag from "../ui/tag";
|
||||||
|
|
||||||
@@ -120,12 +119,6 @@ export const Title = () => {
|
|||||||
}}
|
}}
|
||||||
color={currentScreen.color || colors.heading}
|
color={currentScreen.color || colors.heading}
|
||||||
>
|
>
|
||||||
{isTopic ? (
|
|
||||||
<Paragraph numberOfLines={1} size={SIZE.xs + 1}>
|
|
||||||
{notebook?.title}
|
|
||||||
{"\n"}
|
|
||||||
</Paragraph>
|
|
||||||
) : null}
|
|
||||||
{isTag ? (
|
{isTag ? (
|
||||||
<Heading
|
<Heading
|
||||||
size={isTopic ? SIZE.md + 2 : SIZE.xl}
|
size={isTopic ? SIZE.md + 2 : SIZE.xl}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { View } from "react-native";
|
|||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { notesnook } from "../../../../e2e/test.ids";
|
import { notesnook } from "../../../../e2e/test.ids";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
|
import Notebook from "../../../screens/notebook";
|
||||||
import { TaggedNotes } from "../../../screens/notes/tagged";
|
import { TaggedNotes } from "../../../screens/notes/tagged";
|
||||||
import { TopicNotes } from "../../../screens/notes/topic-notes";
|
import { TopicNotes } from "../../../screens/notes/topic-notes";
|
||||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||||
@@ -39,10 +40,6 @@ import { TimeSince } from "../../ui/time-since";
|
|||||||
import Heading from "../../ui/typography/heading";
|
import Heading from "../../ui/typography/heading";
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
|
|
||||||
const navigateToTopic = (topic) => {
|
|
||||||
TopicNotes.navigate(topic, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
function navigateToTag(item) {
|
function navigateToTag(item) {
|
||||||
const tag = db.tags.tag(item.id);
|
const tag = db.tags.tag(item.id);
|
||||||
if (!tag) return;
|
if (!tag) return;
|
||||||
@@ -55,24 +52,29 @@ const showActionSheet = (item) => {
|
|||||||
|
|
||||||
function getNotebook(item) {
|
function getNotebook(item) {
|
||||||
const isTrash = item.type === "trash";
|
const isTrash = item.type === "trash";
|
||||||
if (isTrash || !item.notebooks || item.notebooks.length < 1) return [];
|
if (isTrash) return [];
|
||||||
|
const items = [];
|
||||||
|
const notebooks = db.relations.to(item, "notebook") || [];
|
||||||
|
|
||||||
return item.notebooks.reduce(function (prev, curr) {
|
for (let notebook of notebooks) {
|
||||||
if (prev && prev.length > 0) return prev;
|
if (items.length > 1) break;
|
||||||
const topicId = curr.topics[0];
|
items.push(notebook);
|
||||||
const notebook = db.notebooks?.notebook(curr.id)?.data;
|
}
|
||||||
if (!notebook) return [];
|
|
||||||
const topic = notebook.topics.find((t) => t.id === topicId);
|
|
||||||
if (!topic) return [];
|
|
||||||
|
|
||||||
return [
|
if (item.notebooks) {
|
||||||
{
|
for (let nb of item.notebooks) {
|
||||||
title: `${notebook?.title} › ${topic?.title}`,
|
if (items.length > 1) break;
|
||||||
notebook: notebook,
|
const notebook = db.notebooks?.notebook(nb.id)?.data;
|
||||||
topic: topic
|
if (!notebook) continue;
|
||||||
|
for (let topicId of nb.topics) {
|
||||||
|
if (items.length > 1) break;
|
||||||
|
const topic = notebook.topics.find((t) => t.id === topicId);
|
||||||
|
if (!topic) continue;
|
||||||
|
items.push(topic);
|
||||||
}
|
}
|
||||||
];
|
}
|
||||||
}, []);
|
}
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoteItem = ({
|
const NoteItem = ({
|
||||||
@@ -88,10 +90,11 @@ const NoteItem = ({
|
|||||||
);
|
);
|
||||||
const compactMode = notesListMode === "compact";
|
const compactMode = notesListMode === "compact";
|
||||||
const attachmentCount = db.attachments?.ofNote(item.id, "all")?.length || 0;
|
const attachmentCount = db.attachments?.ofNote(item.id, "all")?.length || 0;
|
||||||
const notebooks = React.useMemo(() => getNotebook(item), [item]);
|
const _update = useRelationStore((state) => state.updater);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
const notebooks = React.useMemo(() => getNotebook(item), [item, _update]);
|
||||||
const reminders = db.relations.from(item, "reminder");
|
const reminders = db.relations.from(item, "reminder");
|
||||||
const reminder = getUpcomingReminder(reminders);
|
const reminder = getUpcomingReminder(reminders);
|
||||||
const _update = useRelationStore((state) => state.updater);
|
|
||||||
const noteColor = COLORS_NOTE[item.color?.toLowerCase()];
|
const noteColor = COLORS_NOTE[item.color?.toLowerCase()];
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -112,12 +115,12 @@ const NoteItem = ({
|
|||||||
flexWrap: "wrap"
|
flexWrap: "wrap"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{notebooks?.map((_item) => (
|
{notebooks?.map((item) => (
|
||||||
<Button
|
<Button
|
||||||
title={_item.title}
|
title={item.title}
|
||||||
key={_item}
|
key={item}
|
||||||
height={25}
|
height={25}
|
||||||
icon="book-outline"
|
icon={item.type === "topic" ? "bookmark" : "book-outline"}
|
||||||
type="grayBg"
|
type="grayBg"
|
||||||
fontSize={SIZE.xs}
|
fontSize={SIZE.xs}
|
||||||
iconSize={SIZE.sm}
|
iconSize={SIZE.sm}
|
||||||
@@ -132,7 +135,13 @@ const NoteItem = ({
|
|||||||
paddingHorizontal: 6,
|
paddingHorizontal: 6,
|
||||||
marginBottom: 5
|
marginBottom: 5
|
||||||
}}
|
}}
|
||||||
onPress={() => navigateToTopic(_item.topic)}
|
onPress={() => {
|
||||||
|
if (item.type === "topic") {
|
||||||
|
TopicNotes.navigate(item, true);
|
||||||
|
} else {
|
||||||
|
Notebook.navigate(item);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ const RenderItem = ({ item, index, type, ...restArgs }) => {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((t) => t !== null) || [];
|
.filter((t) => t !== null) || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
item={item}
|
item={item}
|
||||||
@@ -103,7 +104,9 @@ const List = ({
|
|||||||
ListHeader,
|
ListHeader,
|
||||||
warning,
|
warning,
|
||||||
isSheet = false,
|
isSheet = false,
|
||||||
onMomentumScrollEnd
|
onMomentumScrollEnd,
|
||||||
|
handlers,
|
||||||
|
ScrollComponent
|
||||||
}) => {
|
}) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const scrollRef = useRef();
|
const scrollRef = useRef();
|
||||||
@@ -158,7 +161,7 @@ const List = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const _keyExtractor = (item) => item.id || item.title;
|
const _keyExtractor = (item) => item.id || item.title;
|
||||||
|
const ListView = ScrollComponent ? ScrollComponent : FlashList;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
@@ -167,7 +170,8 @@ const List = ({
|
|||||||
}}
|
}}
|
||||||
entering={type === "search" ? undefined : FadeInDown}
|
entering={type === "search" ? undefined : FadeInDown}
|
||||||
>
|
>
|
||||||
<FlashList
|
<ListView
|
||||||
|
{...handlers}
|
||||||
style={styles}
|
style={styles}
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
testID={notesnook.list.id}
|
testID={notesnook.list.id}
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Platform, View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import { FlatList } from "react-native-actions-sheet";
|
||||||
import { db } from "../../common/database";
|
import { db } from "../../common/database";
|
||||||
import { DDS } from "../../services/device-detection";
|
import { DDS } from "../../services/device-detection";
|
||||||
import { presentSheet } from "../../services/event-manager";
|
import { presentSheet } from "../../services/event-manager";
|
||||||
@@ -37,7 +36,6 @@ import { Items } from "./items";
|
|||||||
import Notebooks from "./notebooks";
|
import Notebooks from "./notebooks";
|
||||||
import { Synced } from "./synced";
|
import { Synced } from "./synced";
|
||||||
import { Tags, TagStrip } from "./tags";
|
import { Tags, TagStrip } from "./tags";
|
||||||
|
|
||||||
const Line = ({ top = 6, bottom = 6 }) => {
|
const Line = ({ top = 6, bottom = 6 }) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
return (
|
return (
|
||||||
@@ -53,20 +51,11 @@ const Line = ({ top = 6, bottom = 6 }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Properties = ({
|
export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||||
close = () => {},
|
|
||||||
item,
|
|
||||||
buttons = [],
|
|
||||||
getRef
|
|
||||||
}) => {
|
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const alias = item.alias || item.title;
|
const alias = item.alias || item.title;
|
||||||
const isColor = !!COLORS_NOTE[item.title];
|
const isColor = !!COLORS_NOTE[item.title];
|
||||||
|
|
||||||
const onScrollEnd = () => {
|
|
||||||
getRef().current?.handleChildScrollEnd();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!item || !item.id) {
|
if (!item || !item.id) {
|
||||||
return (
|
return (
|
||||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||||
@@ -75,89 +64,92 @@ export const Properties = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<FlatList
|
||||||
nestedScrollEnabled
|
|
||||||
onMomentumScrollEnd={onScrollEnd}
|
|
||||||
keyboardShouldPersistTaps="always"
|
keyboardShouldPersistTaps="always"
|
||||||
keyboardDismissMode="none"
|
keyboardDismissMode="none"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: colors.bg,
|
backgroundColor: colors.bg,
|
||||||
paddingHorizontal: 0,
|
|
||||||
borderBottomRightRadius: DDS.isLargeTablet() ? 10 : 1,
|
borderBottomRightRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||||
borderBottomLeftRadius: DDS.isLargeTablet() ? 10 : 1,
|
borderBottomLeftRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||||
maxHeight: "100%"
|
maxHeight: "100%"
|
||||||
}}
|
}}
|
||||||
>
|
data={[0]}
|
||||||
<View
|
keyExtractor={() => "properties-scroll-item"}
|
||||||
style={{
|
renderItem={() => (
|
||||||
paddingHorizontal: 12,
|
<View>
|
||||||
marginTop: 5,
|
<View
|
||||||
zIndex: 10
|
style={{
|
||||||
}}
|
paddingHorizontal: 12,
|
||||||
>
|
marginTop: 5,
|
||||||
<Heading size={SIZE.lg}>
|
zIndex: 10
|
||||||
{item.type === "tag" && !isColor ? (
|
}}
|
||||||
<Heading size={SIZE.xl} color={colors.accent}>
|
>
|
||||||
#
|
<Heading size={SIZE.lg}>
|
||||||
|
{item.type === "tag" && !isColor ? (
|
||||||
|
<Heading size={SIZE.xl} color={colors.accent}>
|
||||||
|
#
|
||||||
|
</Heading>
|
||||||
|
) : null}
|
||||||
|
{alias}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
) : null}
|
{item.type === "note" ? (
|
||||||
{alias}
|
<TagStrip close={close} item={item} />
|
||||||
</Heading>
|
) : null}
|
||||||
|
|
||||||
{item.type === "note" ? <TagStrip close={close} item={item} /> : null}
|
{item.type === "reminder" ? (
|
||||||
|
<ReminderTime
|
||||||
|
reminder={item}
|
||||||
|
style={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
borderWidth: 0,
|
||||||
|
height: 30,
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
paddingHorizontal: 0
|
||||||
|
}}
|
||||||
|
fontSize={SIZE.xs + 1}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<Line top={12} />
|
||||||
|
|
||||||
{item.type === "reminder" ? (
|
<DateMeta item={item} />
|
||||||
<ReminderTime
|
<Line bottom={0} />
|
||||||
reminder={item}
|
{item.type === "note" ? <Tags close={close} item={item} /> : null}
|
||||||
|
|
||||||
|
<View
|
||||||
style={{
|
style={{
|
||||||
justifyContent: "flex-start",
|
paddingHorizontal: 12
|
||||||
borderWidth: 0,
|
}}
|
||||||
height: 30,
|
>
|
||||||
alignSelf: "flex-start",
|
{item.notebooks ? <Notebooks note={item} close={close} /> : null}
|
||||||
backgroundColor: "transparent",
|
</View>
|
||||||
paddingHorizontal: 0
|
|
||||||
|
<Items
|
||||||
|
item={item}
|
||||||
|
buttons={buttons}
|
||||||
|
close={() => {
|
||||||
|
close();
|
||||||
|
setTimeout(() => {
|
||||||
|
SearchService.updateAndSearch();
|
||||||
|
}, 1000);
|
||||||
}}
|
}}
|
||||||
fontSize={SIZE.xs + 1}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
<Synced item={item} close={close} />
|
||||||
</View>
|
<DevMode item={item} />
|
||||||
<Line top={12} />
|
|
||||||
|
|
||||||
<DateMeta item={item} />
|
{DDS.isTab ? (
|
||||||
<Line bottom={0} />
|
<View
|
||||||
{item.type === "note" ? <Tags close={close} item={item} /> : null}
|
style={{
|
||||||
|
height: 20
|
||||||
<View
|
}}
|
||||||
style={{
|
/>
|
||||||
paddingHorizontal: 12
|
) : null}
|
||||||
}}
|
<SheetProvider context="properties" />
|
||||||
>
|
</View>
|
||||||
{item.notebooks ? <Notebooks note={item} close={close} /> : null}
|
)}
|
||||||
</View>
|
/>
|
||||||
|
|
||||||
<Items
|
|
||||||
item={item}
|
|
||||||
buttons={buttons}
|
|
||||||
close={() => {
|
|
||||||
close();
|
|
||||||
setTimeout(() => {
|
|
||||||
SearchService.updateAndSearch();
|
|
||||||
}, 1000);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Synced item={item} close={close} />
|
|
||||||
<DevMode item={item} />
|
|
||||||
|
|
||||||
{DDS.isTab ? (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
height: 20
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<SheetProvider context="properties" />
|
|
||||||
</ScrollView>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,12 +211,13 @@ Properties.present = (item, buttons = [], isSheet) => {
|
|||||||
if (!props[0]) return;
|
if (!props[0]) return;
|
||||||
presentSheet({
|
presentSheet({
|
||||||
context: isSheet ? "local" : undefined,
|
context: isSheet ? "local" : undefined,
|
||||||
|
enableGesturesInScrollView: true,
|
||||||
component: (ref, close) => (
|
component: (ref, close) => (
|
||||||
<Properties
|
<Properties
|
||||||
close={() => {
|
close={() => {
|
||||||
close();
|
close();
|
||||||
}}
|
}}
|
||||||
getRef={() => ref}
|
actionSheetRef={ref}
|
||||||
item={props[0]}
|
item={props[0]}
|
||||||
buttons={props[1]}
|
buttons={props[1]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { View } from "react-native";
|
import { FlatList, ScrollView, View } from "react-native";
|
||||||
import { FlatList } from "react-native-gesture-handler";
|
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { useActions } from "../../hooks/use-actions";
|
import { useActions } from "../../hooks/use-actions";
|
||||||
import { DDS } from "../../services/device-detection";
|
import { DDS } from "../../services/device-detection";
|
||||||
@@ -85,8 +84,9 @@ export const Items = ({ item, buttons, close }) => {
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderColumnItem = ({ item }) => (
|
const renderColumnItem = (item) => (
|
||||||
<Button
|
<Button
|
||||||
|
key={item.name + item.title}
|
||||||
buttonType={{
|
buttonType={{
|
||||||
text: item.on
|
text: item.on
|
||||||
? colors.accent
|
? colors.accent
|
||||||
@@ -108,7 +108,7 @@ export const Items = ({ item, buttons, close }) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderTopBarItem = ({ item }) => {
|
const renderTopBarItem = (item) => {
|
||||||
return (
|
return (
|
||||||
<PressableButton
|
<PressableButton
|
||||||
onPress={item.func}
|
onPress={item.func}
|
||||||
@@ -169,7 +169,7 @@ export const Items = ({ item, buttons, close }) => {
|
|||||||
"lock-unlock",
|
"lock-unlock",
|
||||||
"publish"
|
"publish"
|
||||||
];
|
];
|
||||||
const bottomBarItems = data.filter(
|
const topBarItems = data.filter(
|
||||||
(item) => topBarItemsList.indexOf(item.id) > -1
|
(item) => topBarItemsList.indexOf(item.id) > -1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -178,21 +178,19 @@ export const Items = ({ item, buttons, close }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const topBarItemWidth =
|
const topBarItemWidth =
|
||||||
(width - (bottomBarItems.length * 10 + 14)) / bottomBarItems.length;
|
(width - (topBarItems.length * 10 + 14)) / topBarItems.length;
|
||||||
|
|
||||||
return item.type === "note" ? (
|
return item.type === "note" ? (
|
||||||
<>
|
<>
|
||||||
<FlatList
|
<ScrollView
|
||||||
data={bottomBarItems}
|
|
||||||
keyExtractor={(item) => item.title}
|
|
||||||
horizontal
|
horizontal
|
||||||
disableVirtualization={true}
|
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingTop: 12
|
paddingTop: 12
|
||||||
}}
|
}}
|
||||||
renderItem={renderTopBarItem}
|
>
|
||||||
/>
|
{topBarItems.map(renderTopBarItem)}
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
<FlatList
|
<FlatList
|
||||||
data={bottomGridItems}
|
data={bottomGridItems}
|
||||||
@@ -216,11 +214,6 @@ export const Items = ({ item, buttons, close }) => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<FlatList
|
<View data={data}>{data.map(renderColumnItem)}</View>
|
||||||
data={data}
|
|
||||||
keyExtractor={(item) => item.title}
|
|
||||||
renderItem={renderColumnItem}
|
|
||||||
disableVirtualization={true}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,11 +21,10 @@ import React from "react";
|
|||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { db } from "../../common/database";
|
import { db } from "../../common/database";
|
||||||
import { TaggedNotes } from "../../screens/notes/tagged";
|
import { TaggedNotes } from "../../screens/notes/tagged";
|
||||||
import { eSendEvent } from "../../services/event-manager";
|
|
||||||
import { useThemeStore } from "../../stores/use-theme-store";
|
import { useThemeStore } from "../../stores/use-theme-store";
|
||||||
import { eOpenTagsDialog } from "../../utils/events";
|
|
||||||
import { SIZE } from "../../utils/size";
|
import { SIZE } from "../../utils/size";
|
||||||
import { sleep } from "../../utils/time";
|
import { sleep } from "../../utils/time";
|
||||||
|
import ManageTagsSheet from "../sheets/manage-tags";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { ColorTags } from "./color-tags";
|
import { ColorTags } from "./color-tags";
|
||||||
export const Tags = ({ item, close }) => {
|
export const Tags = ({ item, close }) => {
|
||||||
@@ -46,9 +45,7 @@ export const Tags = ({ item, close }) => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
close();
|
ManageTagsSheet.present(item);
|
||||||
await sleep(300);
|
|
||||||
eSendEvent(eOpenTagsDialog, item);
|
|
||||||
}}
|
}}
|
||||||
buttonType={{
|
buttonType={{
|
||||||
text: colors.accent
|
text: colors.accent
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ export const SelectionHeader = React.memo(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Heading size={SIZE.md} color={colors.accent}>
|
<Heading size={SIZE.md} color={colors.accent}>
|
||||||
{selectedItemsList.length + " Selected"}
|
{selectedItemsList.length}
|
||||||
</Heading>
|
</Heading>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import Paragraph from "../ui/typography/paragraph";
|
|||||||
const SheetProvider = ({ context = "global" }) => {
|
const SheetProvider = ({ context = "global" }) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [dialogData, setDialogData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const actionSheetRef = useRef();
|
const actionSheetRef = useRef();
|
||||||
const editor = useRef({
|
const editor = useRef({
|
||||||
refocus: false
|
refocus: false
|
||||||
@@ -52,42 +52,35 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
}, [close, open, visible]);
|
}, [close, open, visible]);
|
||||||
|
|
||||||
const open = useCallback(
|
const open = useCallback(
|
||||||
async (data) => {
|
async (payload) => {
|
||||||
if (!data.context) data.context = "global";
|
if (!payload.context) payload.context = "global";
|
||||||
if (data.context !== context) return;
|
if (payload.context !== context) return;
|
||||||
if (visible || dialogData) {
|
setData(payload);
|
||||||
setDialogData(null);
|
|
||||||
setVisible(false);
|
|
||||||
await sleep(0);
|
|
||||||
}
|
|
||||||
setDialogData(data);
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
if (data.editor) {
|
if (payload.editor) {
|
||||||
editor.current.refocus = false;
|
editor.current.refocus = false;
|
||||||
if (editorState().keyboardState) {
|
if (editorState().keyboardState) {
|
||||||
// tiny.call(EditorWebView, tiny.cacheRange + tiny.blur);
|
|
||||||
editor.current.refocus = true;
|
editor.current.refocus = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[context, dialogData, visible]
|
[context]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (visible && dialogData) {
|
if (visible && data) {
|
||||||
if (dialogData.editor) await sleep(100);
|
if (data.editor) await sleep(100);
|
||||||
actionSheetRef.current?.setModalVisible(true);
|
actionSheetRef.current?.setModalVisible(true);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (editor.current?.refocus) {
|
if (editor.current?.refocus) {
|
||||||
editorState().isFocused = true;
|
editorState().isFocused = true;
|
||||||
// tiny.call(EditorWebView, tiny.restoreRange + tiny.clearRange);
|
|
||||||
editor.current.refocus = false;
|
editor.current.refocus = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [visible, dialogData]);
|
}, [visible, data]);
|
||||||
|
|
||||||
const close = useCallback(
|
const close = useCallback(
|
||||||
(ctx) => {
|
(ctx) => {
|
||||||
@@ -98,34 +91,30 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
[context]
|
[context]
|
||||||
);
|
);
|
||||||
|
|
||||||
return !visible || !dialogData ? null : (
|
return !visible || !data ? null : (
|
||||||
<SheetWrapper
|
<SheetWrapper
|
||||||
fwdRef={actionSheetRef}
|
fwdRef={actionSheetRef}
|
||||||
gestureEnabled={!dialogData?.progress && !dialogData?.disableClosing}
|
gestureEnabled={!data?.progress && !data?.disableClosing}
|
||||||
closeOnTouchBackdrop={
|
closeOnTouchBackdrop={!data?.progress && !data?.disableClosing}
|
||||||
!dialogData?.progress && !dialogData?.disableClosing
|
|
||||||
}
|
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
dialogData.onClose && dialogData.onClose();
|
data.onClose && data.onClose();
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
setDialogData(null);
|
setData(null);
|
||||||
}}
|
}}
|
||||||
|
enableGesturesInScrollView={data.enableGesturesInScrollView}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
marginBottom:
|
marginBottom:
|
||||||
!dialogData.progress &&
|
!data.progress && !data.icon && !data.title && !data.paragraph
|
||||||
!dialogData.icon &&
|
|
||||||
!dialogData.title &&
|
|
||||||
!dialogData.paragraph
|
|
||||||
? 0
|
? 0
|
||||||
: 10,
|
: 10,
|
||||||
paddingHorizontal: 12
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{dialogData?.progress ? (
|
{data?.progress ? (
|
||||||
<ActivityIndicator
|
<ActivityIndicator
|
||||||
style={{
|
style={{
|
||||||
marginTop: 15
|
marginTop: 15
|
||||||
@@ -135,47 +124,48 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{dialogData?.icon ? (
|
{data?.icon ? (
|
||||||
<Icon
|
<Icon
|
||||||
color={colors[dialogData.iconColor] || colors.accent}
|
color={colors[data.iconColor] || colors.accent}
|
||||||
name={dialogData.icon}
|
name={data.icon}
|
||||||
size={50}
|
size={50}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{dialogData?.title ? <Heading> {dialogData?.title}</Heading> : null}
|
{data?.title ? <Heading> {data?.title}</Heading> : null}
|
||||||
|
|
||||||
{dialogData?.paragraph ? (
|
{data?.paragraph ? (
|
||||||
<Paragraph style={{ textAlign: "center" }}>
|
<Paragraph style={{ textAlign: "center" }}>
|
||||||
{dialogData?.paragraph}
|
{data?.paragraph}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{typeof dialogData.component === "function"
|
{typeof data.component === "function"
|
||||||
? dialogData.component(
|
? data.component(
|
||||||
actionSheetRef,
|
actionSheetRef,
|
||||||
() => close(context),
|
() => close(context),
|
||||||
(data) => {
|
(data) => {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
setDialogData((prevData) => {
|
setData((prevData) => {
|
||||||
return {
|
return {
|
||||||
...prevData,
|
...prevData,
|
||||||
...data
|
...data
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
colors
|
||||||
)
|
)
|
||||||
: dialogData.component}
|
: data.component}
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
marginBottom: dialogData.valueArray ? 12 : 0
|
marginBottom: data.valueArray ? 12 : 0
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{dialogData.valueArray &&
|
{data.valueArray &&
|
||||||
dialogData.valueArray.map((v) => (
|
data.valueArray.map((v) => (
|
||||||
<Button
|
<Button
|
||||||
title={v}
|
title={v}
|
||||||
type="gray"
|
type="gray"
|
||||||
@@ -197,12 +187,12 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
paddingHorizontal: 12
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{dialogData?.action ? (
|
{data?.action ? (
|
||||||
<Button
|
<Button
|
||||||
onPress={dialogData.action}
|
onPress={data.action}
|
||||||
key={dialogData.actionText}
|
key={data.actionText}
|
||||||
title={dialogData.actionText}
|
title={data.actionText}
|
||||||
accentColor={dialogData.iconColor || "accent"}
|
accentColor={data.iconColor || "accent"}
|
||||||
accentText="light"
|
accentText="light"
|
||||||
type="accent"
|
type="accent"
|
||||||
height={45}
|
height={45}
|
||||||
@@ -214,8 +204,8 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{dialogData?.actionsArray &&
|
{data?.actionsArray &&
|
||||||
dialogData?.actionsArray.map((item) => (
|
data?.actionsArray.map((item) => (
|
||||||
<Button
|
<Button
|
||||||
onPress={item.action}
|
onPress={item.action}
|
||||||
key={item.accentText}
|
key={item.accentText}
|
||||||
@@ -231,7 +221,7 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{dialogData?.learnMore ? (
|
{data?.learnMore ? (
|
||||||
<Paragraph
|
<Paragraph
|
||||||
style={{
|
style={{
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
@@ -239,7 +229,7 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
textDecorationLine: "underline"
|
textDecorationLine: "underline"
|
||||||
}}
|
}}
|
||||||
size={SIZE.xs}
|
size={SIZE.xs}
|
||||||
onPress={dialogData.learnMorePress}
|
onPress={data.learnMorePress}
|
||||||
color={colors.icon}
|
color={colors.icon}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
@@ -247,7 +237,7 @@ const SheetProvider = ({ context = "global" }) => {
|
|||||||
name="information-outline"
|
name="information-outline"
|
||||||
size={SIZE.xs}
|
size={SIZE.xs}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
{dialogData.learnMore}
|
{data.learnMore}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -25,41 +25,34 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View
|
View
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { FlatList } from "react-native-gesture-handler";
|
|
||||||
import { notesnook } from "../../../../e2e/test.ids";
|
import { notesnook } from "../../../../e2e/test.ids";
|
||||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
|
||||||
import { DDS } from "../../../services/device-detection";
|
|
||||||
import {
|
|
||||||
eSubscribeEvent,
|
|
||||||
eUnSubscribeEvent,
|
|
||||||
ToastEvent
|
|
||||||
} from "../../../services/event-manager";
|
|
||||||
import Navigation from "../../../services/navigation";
|
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import { DDS } from "../../../services/device-detection";
|
||||||
eCloseAddNotebookDialog,
|
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||||
eOpenAddNotebookDialog
|
import Navigation from "../../../services/navigation";
|
||||||
} from "../../../utils/events";
|
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||||
|
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||||
import { ph, pv, SIZE } from "../../../utils/size";
|
import { ph, pv, SIZE } from "../../../utils/size";
|
||||||
import { sleep } from "../../../utils/time";
|
import { sleep } from "../../../utils/time";
|
||||||
import { IconButton } from "../../ui/icon-button";
|
|
||||||
import { Button } from "../../ui/button";
|
|
||||||
import DialogHeader from "../../dialog/dialog-header";
|
import DialogHeader from "../../dialog/dialog-header";
|
||||||
|
import { Button } from "../../ui/button";
|
||||||
|
import { IconButton } from "../../ui/icon-button";
|
||||||
import Input from "../../ui/input";
|
import Input from "../../ui/input";
|
||||||
import { MoveNotes } from "../move-notes/movenote";
|
|
||||||
import Seperator from "../../ui/seperator";
|
import Seperator from "../../ui/seperator";
|
||||||
import SheetWrapper from "../../ui/sheet";
|
import { MoveNotes } from "../move-notes/movenote";
|
||||||
import { Toast } from "../../toast";
|
import { FlatList } from "react-native-actions-sheet";
|
||||||
|
|
||||||
let refs = [];
|
let refs = [];
|
||||||
|
|
||||||
export class AddNotebookSheet extends React.Component {
|
export class AddNotebookSheet extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
refs = [];
|
||||||
this.state = {
|
this.state = {
|
||||||
notebook: null,
|
notebook: props.notebook,
|
||||||
visible: false,
|
topics:
|
||||||
topics: [],
|
props.notebook?.topics?.map((item) => {
|
||||||
|
return item.title;
|
||||||
|
}) || [],
|
||||||
description: null,
|
description: null,
|
||||||
titleFocused: false,
|
titleFocused: false,
|
||||||
descFocused: false,
|
descFocused: false,
|
||||||
@@ -68,13 +61,14 @@ export class AddNotebookSheet extends React.Component {
|
|||||||
editTopic: false,
|
editTopic: false,
|
||||||
loading: false
|
loading: false
|
||||||
};
|
};
|
||||||
this.title = null;
|
|
||||||
this.description = null;
|
this.title = props.notebook?.title;
|
||||||
|
this.description = props.notebook?.description;
|
||||||
this.listRef;
|
this.listRef;
|
||||||
this.prevItem = null;
|
this.prevItem = null;
|
||||||
this.prevIndex = null;
|
this.prevIndex = null;
|
||||||
this.currentSelectedInput = null;
|
this.currentSelectedInput = null;
|
||||||
this.id = null;
|
this.id = props.notebook?.id;
|
||||||
this.backPressCount = 0;
|
this.backPressCount = 0;
|
||||||
this.currentInputValue = null;
|
this.currentInputValue = null;
|
||||||
this.titleRef;
|
this.titleRef;
|
||||||
@@ -83,57 +77,22 @@ export class AddNotebookSheet extends React.Component {
|
|||||||
this.hiddenInput = createRef();
|
this.hiddenInput = createRef();
|
||||||
this.topicInputRef = createRef();
|
this.topicInputRef = createRef();
|
||||||
this.addingTopic = false;
|
this.addingTopic = false;
|
||||||
this.actionSheetRef = createRef();
|
this.actionSheetRef = props.actionSheetRef;
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
eSubscribeEvent(eOpenAddNotebookDialog, this.open);
|
|
||||||
eSubscribeEvent(eCloseAddNotebookDialog, this.close);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
eUnSubscribeEvent(eOpenAddNotebookDialog, this.open);
|
refs = [];
|
||||||
eUnSubscribeEvent(eCloseAddNotebookDialog, this.close);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open = (notebook) => {
|
componentDidMount() {
|
||||||
refs = [];
|
sleep(300).then(() => {
|
||||||
|
!this.state.notebook && this.titleRef?.focus();
|
||||||
if (notebook) {
|
|
||||||
let topicsList = [];
|
|
||||||
notebook.topics.forEach((item) => {
|
|
||||||
topicsList.push(item.title);
|
|
||||||
});
|
|
||||||
this.id = notebook.id;
|
|
||||||
this.title = notebook.title;
|
|
||||||
this.description = notebook.description;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
topics: [...topicsList],
|
|
||||||
visible: true,
|
|
||||||
notebook: notebook
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
visible: true,
|
|
||||||
notebook: null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
sleep(100).then(() => {
|
|
||||||
this.actionSheetRef.current?.show();
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
close = () => {
|
close = () => {
|
||||||
this.actionSheetRef.current?.hide();
|
|
||||||
refs = [];
|
refs = [];
|
||||||
this.prevIndex = null;
|
this.props.close();
|
||||||
this.prevItem = null;
|
|
||||||
this.currentSelectedInput = null;
|
|
||||||
this.title = null;
|
|
||||||
this.description = null;
|
|
||||||
this.currentInputValue = null;
|
|
||||||
this.id = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onDelete = (index) => {
|
onDelete = (index) => {
|
||||||
@@ -240,15 +199,8 @@ export class AddNotebookSheet extends React.Component {
|
|||||||
"Notebooks",
|
"Notebooks",
|
||||||
"Notebook"
|
"Notebook"
|
||||||
);
|
);
|
||||||
|
useRelationStore.getState().update();
|
||||||
this.setState({
|
MoveNotes.present(db.notebooks.notebook(newNotebookId).data);
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
this.close();
|
|
||||||
await sleep(300);
|
|
||||||
if (!notebook) {
|
|
||||||
MoveNotes.present(db.notebooks.notebook(newNotebookId).data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit = (forward = true) => {
|
onSubmit = (forward = true) => {
|
||||||
@@ -301,166 +253,139 @@ export class AddNotebookSheet extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { colors } = this.props;
|
const { colors } = this.props;
|
||||||
const { topics, visible, topicInputFocused, notebook } = this.state;
|
const { topics, topicInputFocused, notebook } = this.state;
|
||||||
if (!visible) return null;
|
|
||||||
return (
|
return (
|
||||||
<SheetWrapper
|
<View
|
||||||
onOpen={async () => {
|
style={{
|
||||||
this.topicsToDelete = [];
|
maxHeight: DDS.isTab ? "90%" : "96%",
|
||||||
await sleep(300);
|
borderRadius: DDS.isTab ? 5 : 0,
|
||||||
!this.state.notebook && this.titleRef?.focus();
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
fwdRef={this.actionSheetRef}
|
|
||||||
onClose={() => {
|
|
||||||
this.close();
|
|
||||||
this.setState({
|
|
||||||
visible: false,
|
|
||||||
topics: [],
|
|
||||||
descFocused: false,
|
|
||||||
titleFocused: false,
|
|
||||||
editTopic: false,
|
|
||||||
notebook: null
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
statusBarTranslucent={false}
|
|
||||||
onRequestClose={this.close}
|
|
||||||
>
|
>
|
||||||
<View
|
<TextInput
|
||||||
|
ref={this.hiddenInput}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: DDS.isTab ? "90%" : "96%",
|
width: 1,
|
||||||
borderRadius: DDS.isTab ? 5 : 0,
|
height: 1,
|
||||||
paddingHorizontal: 12
|
opacity: 0,
|
||||||
|
position: "absolute"
|
||||||
}}
|
}}
|
||||||
>
|
blurOnSubmit={false}
|
||||||
<TextInput
|
/>
|
||||||
ref={this.hiddenInput}
|
<DialogHeader
|
||||||
style={{
|
title={
|
||||||
width: 1,
|
notebook && notebook.dateCreated ? "Edit Notebook" : "New Notebook"
|
||||||
height: 1,
|
}
|
||||||
opacity: 0,
|
paragraph={
|
||||||
position: "absolute"
|
notebook && notebook.dateCreated
|
||||||
}}
|
? "You are editing " + this.title + " notebook."
|
||||||
blurOnSubmit={false}
|
: "Notebooks are the best way to organize your notes."
|
||||||
/>
|
}
|
||||||
<DialogHeader
|
/>
|
||||||
title={
|
<Seperator half />
|
||||||
notebook && notebook.dateCreated
|
|
||||||
? "Edit Notebook"
|
<Input
|
||||||
: "New Notebook"
|
fwdRef={(ref) => (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}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
fwdRef={(ref) => (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}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
fwdRef={this.topicInputRef}
|
||||||
|
testID={notesnook.ids.dialogs.notebook.inputs.topic}
|
||||||
|
onChangeText={(value) => {
|
||||||
|
this.currentInputValue = value;
|
||||||
|
if (this.prevItem !== null) {
|
||||||
|
refs[this.prevIndex].setNativeProps({
|
||||||
|
text: value,
|
||||||
|
style: {
|
||||||
|
borderBottomColor: colors.accent
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
paragraph={
|
}}
|
||||||
notebook && notebook.dateCreated
|
returnKeyLabel="Done"
|
||||||
? "You are editing " + this.title + " notebook."
|
returnKeyType="done"
|
||||||
: "Notebooks are the best way to organize your notes."
|
onSubmit={() => {
|
||||||
}
|
this.onSubmit();
|
||||||
/>
|
}}
|
||||||
<Seperator half />
|
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"
|
||||||
|
/>
|
||||||
|
|
||||||
<Input
|
<FlatList
|
||||||
fwdRef={(ref) => (this.titleRef = ref)}
|
data={topics}
|
||||||
testID={notesnook.ids.dialogs.notebook.inputs.title}
|
ref={(ref) => (this.listRef = ref)}
|
||||||
onChangeText={(value) => {
|
nestedScrollEnabled
|
||||||
this.title = value;
|
keyExtractor={(item, index) => item + index.toString()}
|
||||||
}}
|
keyboardShouldPersistTaps="always"
|
||||||
placeholder="Enter a title"
|
keyboardDismissMode="interactive"
|
||||||
onSubmit={() => {
|
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||||
this.descriptionRef.focus();
|
renderItem={({ item, index }) => (
|
||||||
}}
|
<TopicItem
|
||||||
returnKeyLabel="Next"
|
item={item}
|
||||||
returnKeyType="next"
|
onPress={(item, index) => {
|
||||||
defaultValue={notebook ? notebook.title : null}
|
this.prevIndex = index;
|
||||||
/>
|
this.prevItem = item;
|
||||||
|
this.topicInputRef.current?.setNativeProps({
|
||||||
<Input
|
text: item
|
||||||
fwdRef={(ref) => (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}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
fwdRef={this.topicInputRef}
|
|
||||||
testID={notesnook.ids.dialogs.notebook.inputs.topic}
|
|
||||||
onChangeText={(value) => {
|
|
||||||
this.currentInputValue = value;
|
|
||||||
if (this.prevItem !== null) {
|
|
||||||
refs[this.prevIndex].setNativeProps({
|
|
||||||
text: value,
|
|
||||||
style: {
|
|
||||||
borderBottomColor: colors.accent
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
this.topicInputRef.current?.focus();
|
||||||
}}
|
this.currentInputValue = item;
|
||||||
returnKeyLabel="Done"
|
this.setState({
|
||||||
returnKeyType="done"
|
editTopic: true
|
||||||
onSubmit={() => {
|
});
|
||||||
this.onSubmit();
|
}}
|
||||||
}}
|
onDelete={this.onDelete}
|
||||||
blurOnSubmit={false}
|
index={index}
|
||||||
button={{
|
colors={colors}
|
||||||
testID: "topic-add-button",
|
/>
|
||||||
icon: this.state.editTopic ? "check" : "plus",
|
)}
|
||||||
onPress: this.onSubmit,
|
/>
|
||||||
color: topicInputFocused ? colors.accent : colors.icon
|
<Seperator />
|
||||||
}}
|
<Button
|
||||||
placeholder="Add a topic"
|
width="100%"
|
||||||
/>
|
height={50}
|
||||||
|
fontSize={SIZE.md}
|
||||||
<FlatList
|
title={
|
||||||
data={topics}
|
notebook && notebook.dateCreated
|
||||||
ref={(ref) => (this.listRef = ref)}
|
? "Save changes"
|
||||||
nestedScrollEnabled
|
: "Create notebook"
|
||||||
keyExtractor={(item, index) => item + index.toString()}
|
}
|
||||||
onMomentumScrollEnd={() => {
|
type="accent"
|
||||||
this.actionSheetRef.current?.handleChildScrollEnd();
|
onPress={this.addNewNotebook}
|
||||||
}}
|
/>
|
||||||
keyboardShouldPersistTaps="always"
|
{/*
|
||||||
keyboardDismissMode="interactive"
|
|
||||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
|
||||||
renderItem={({ item, index }) => (
|
|
||||||
<TopicItem
|
|
||||||
item={item}
|
|
||||||
onPress={(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}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Seperator />
|
|
||||||
<Button
|
|
||||||
width="100%"
|
|
||||||
height={50}
|
|
||||||
fontSize={SIZE.md}
|
|
||||||
title={
|
|
||||||
notebook && notebook.dateCreated
|
|
||||||
? "Save changes"
|
|
||||||
: "Create notebook"
|
|
||||||
}
|
|
||||||
type="accent"
|
|
||||||
onPress={this.addNewNotebook}
|
|
||||||
/>
|
|
||||||
{/*
|
|
||||||
{Platform.OS === 'ios' && (
|
{Platform.OS === 'ios' && (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -468,14 +393,24 @@ export class AddNotebookSheet extends React.Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)} */}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Toast context="local" />
|
|
||||||
</SheetWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddNotebookSheet.present = (notebook) => {
|
||||||
|
presentSheet({
|
||||||
|
component: (ref, close, _update, colors) => (
|
||||||
|
<AddNotebookSheet
|
||||||
|
actionSheetRef={ref}
|
||||||
|
notebook={notebook}
|
||||||
|
close={close}
|
||||||
|
colors={colors}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const TopicItem = ({ item, index, colors, onPress, onDelete }) => {
|
const TopicItem = ({ item, index, colors, onPress, onDelete }) => {
|
||||||
const topicRef = (ref) => (refs[index] = ref);
|
const topicRef = (ref) => (refs[index] = ref);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { FlatList } from "react-native-gesture-handler";
|
import { FlatList } from "react-native-actions-sheet";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import { ListHeaderInputItem } from "./list-header-item.js";
|
import { ListHeaderInputItem } from "./list-header-item.js";
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ export const FilteredList = ({
|
|||||||
itemType,
|
itemType,
|
||||||
onAddItem,
|
onAddItem,
|
||||||
hasHeaderSearch,
|
hasHeaderSearch,
|
||||||
|
listRef,
|
||||||
...restProps
|
...restProps
|
||||||
}) => {
|
}) => {
|
||||||
const [filtered, setFiltered] = useState(data);
|
const [filtered, setFiltered] = useState(data);
|
||||||
@@ -56,6 +57,7 @@ export const FilteredList = ({
|
|||||||
<FlatList
|
<FlatList
|
||||||
{...restProps}
|
{...restProps}
|
||||||
data={filtered}
|
data={filtered}
|
||||||
|
ref={listRef}
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
hasHeaderSearch ? (
|
hasHeaderSearch ? (
|
||||||
<ListHeaderInputItem
|
<ListHeaderInputItem
|
||||||
|
|||||||
@@ -17,91 +17,37 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
createRef,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState
|
|
||||||
} from "react";
|
|
||||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||||
eSubscribeEvent,
|
|
||||||
eUnSubscribeEvent,
|
|
||||||
ToastEvent
|
|
||||||
} from "../../../services/event-manager";
|
|
||||||
import Navigation from "../../../services/navigation";
|
import Navigation from "../../../services/navigation";
|
||||||
import SearchService from "../../../services/search";
|
import SearchService from "../../../services/search";
|
||||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
import { eOpenMoveNoteDialog } from "../../../utils/events";
|
|
||||||
import { Dialog } from "../../dialog";
|
import { Dialog } from "../../dialog";
|
||||||
import DialogHeader from "../../dialog/dialog-header";
|
import DialogHeader from "../../dialog/dialog-header";
|
||||||
import { presentDialog } from "../../dialog/functions";
|
import { presentDialog } from "../../dialog/functions";
|
||||||
import { Button } from "../../ui/button";
|
import { Button } from "../../ui/button";
|
||||||
import SheetWrapper from "../../ui/sheet";
|
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
import { SelectionProvider } from "./context";
|
import { SelectionProvider } from "./context";
|
||||||
import { FilteredList } from "./filtered-list";
|
import { FilteredList } from "./filtered-list";
|
||||||
import { ListItem } from "./list-item";
|
import { ListItem } from "./list-item";
|
||||||
|
|
||||||
const actionSheetRef = createRef();
|
const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||||
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);
|
|
||||||
setNote(null);
|
|
||||||
Navigation.queueRoutesForUpdate(
|
|
||||||
"Notes",
|
|
||||||
"Favorites",
|
|
||||||
"ColoredNotes",
|
|
||||||
"TaggedNotes",
|
|
||||||
"TopicNotes",
|
|
||||||
"Notebooks",
|
|
||||||
"Notebook"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return !visible ? null : (
|
|
||||||
<SheetWrapper fwdRef={actionSheetRef} onClose={_onClose}>
|
|
||||||
<MoveNoteComponent note={note} />
|
|
||||||
</SheetWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddToNotebookSheet;
|
|
||||||
|
|
||||||
const MoveNoteComponent = ({ note }) => {
|
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [multiSelect, setMultiSelect] = useState(false);
|
const [multiSelect, setMultiSelect] = useState(false);
|
||||||
const notebooks = useNotebookStore((state) =>
|
const notebooks = useNotebookStore((state) =>
|
||||||
state.notebooks.filter((n) => n?.type === "notebook")
|
state.notebooks.filter((n) => n?.type === "notebook")
|
||||||
);
|
);
|
||||||
const [edited, setEdited] = useState(false);
|
|
||||||
const selectedItemsList = useSelectionStore(
|
const selectedItemsList = useSelectionStore(
|
||||||
(state) => state.selectedItemsList
|
(state) => state.selectedItemsList
|
||||||
);
|
);
|
||||||
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
|
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
|
||||||
const [itemState, setItemState] = useState({});
|
const [itemState, setItemState] = useState({});
|
||||||
|
|
||||||
const onAddNotebook = async (title) => {
|
const onAddNotebook = async (title) => {
|
||||||
if (!title || title.trim().length === 0) {
|
if (!title || title.trim().length === 0) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
@@ -159,13 +105,13 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
(item) => {
|
(item) => {
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case "notebook": {
|
case "notebook": {
|
||||||
const noteIds = [];
|
const notes = db.relations.from(item, "note");
|
||||||
for (let topic of item.topics) {
|
if (notes.length === 0) return 0;
|
||||||
noteIds.push(...(db.notes?.topicReferences.get(topic.id) || []));
|
|
||||||
}
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
selectedItemsList.forEach((item) =>
|
selectedItemsList.forEach((item) =>
|
||||||
noteIds.indexOf(item.id) > -1 ? count++ : undefined
|
notes.findIndex((note) => note.id === item.id) > -1
|
||||||
|
? count++
|
||||||
|
: undefined
|
||||||
);
|
);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -195,8 +141,7 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
for (let notebook of notebooks) {
|
for (let notebook of notebooks) {
|
||||||
itemState[notebook.id] = state
|
itemState[notebook.id] = state
|
||||||
? state
|
? state
|
||||||
: areAllSelectedItemsInAllTopics(notebook, selectedItemsList) &&
|
: areAllSelectedItemsInNotebook(notebook, selectedItemsList)
|
||||||
getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
|
||||||
? "selected"
|
? "selected"
|
||||||
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
||||||
? "intermediate"
|
? "intermediate"
|
||||||
@@ -231,11 +176,11 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function areAllSelectedItemsInAllTopics(notebook, items) {
|
function areAllSelectedItemsInNotebook(notebook, items) {
|
||||||
|
const notes = db.relations.from(notebook, "note");
|
||||||
|
if (notes.length === 0) return false;
|
||||||
return items.every((item) => {
|
return items.every((item) => {
|
||||||
return notebook.topics.every((topic) => {
|
return notes.find((note) => note.id === item.id);
|
||||||
return db.notes.topicReferences.get(topic.id).indexOf(item.id) > -1;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,25 +195,6 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
const mergeState = {
|
const mergeState = {
|
||||||
[item.id]: state
|
[item.id]: state
|
||||||
};
|
};
|
||||||
const notebooks = db.notebooks.all;
|
|
||||||
const notebook =
|
|
||||||
item.type === "notebook"
|
|
||||||
? item
|
|
||||||
: notebooks.find((n) => n.id === item.notebookId);
|
|
||||||
const intermediate = notebook.topics.some((topic) => {
|
|
||||||
return topic.id === item.id
|
|
||||||
? state === "selected"
|
|
||||||
: itemState[topic.id] === "selected";
|
|
||||||
});
|
|
||||||
if (intermediate) mergeState[notebook.id] = "intermediate";
|
|
||||||
const selected = notebook.topics.every((topic) => {
|
|
||||||
return topic.id === item.id
|
|
||||||
? state === "selected"
|
|
||||||
: itemState[topic.id] === "selected";
|
|
||||||
});
|
|
||||||
if (selected) mergeState[notebook.id] = "selected";
|
|
||||||
if (!selected && !intermediate) mergeState[notebook.id] = "deselected";
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...itemState,
|
...itemState,
|
||||||
...mergeState
|
...mergeState
|
||||||
@@ -314,28 +240,39 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSave = async () => {
|
const onSave = async () => {
|
||||||
|
const noteIds = note ? [note.id] : selectedItemsList.map((n) => n.id);
|
||||||
for (const id in itemState) {
|
for (const id in itemState) {
|
||||||
const item = getItemFromId(id);
|
const item = getItemFromId(id);
|
||||||
if (item.type === "notebook") continue;
|
|
||||||
const noteIds = selectedItemsList.map((n) => n.id);
|
|
||||||
if (itemState[id] === "selected") {
|
if (itemState[id] === "selected") {
|
||||||
await db.notes.addToNotebook(
|
if (item.type === "notebook") {
|
||||||
{
|
for (let noteId of noteIds) {
|
||||||
topic: item.id,
|
db.relations.add(item, { id: noteId, type: "note" });
|
||||||
id: item.notebookId,
|
}
|
||||||
rebuildCache: true
|
} else {
|
||||||
},
|
await db.notes.addToNotebook(
|
||||||
...noteIds
|
{
|
||||||
);
|
topic: item.id,
|
||||||
|
id: item.notebookId,
|
||||||
|
rebuildCache: true
|
||||||
|
},
|
||||||
|
...noteIds
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (itemState[id] === "deselected") {
|
} else if (itemState[id] === "deselected") {
|
||||||
await db.notes.removeFromNotebook(
|
if (item.type === "notebook") {
|
||||||
{
|
for (let noteId of noteIds) {
|
||||||
id: item.notebookId,
|
db.relations.unlink(item, { id: noteId, type: "note" });
|
||||||
topic: item.id,
|
}
|
||||||
rebuildCache: true
|
} else {
|
||||||
},
|
await db.notes.removeFromNotebook(
|
||||||
...noteIds
|
{
|
||||||
);
|
id: item.notebookId,
|
||||||
|
topic: item.id,
|
||||||
|
rebuildCache: true
|
||||||
|
},
|
||||||
|
...noteIds
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Navigation.queueRoutesForUpdate(
|
Navigation.queueRoutesForUpdate(
|
||||||
@@ -343,7 +280,8 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
"Favorites",
|
"Favorites",
|
||||||
"ColoredNotes",
|
"ColoredNotes",
|
||||||
"TaggedNotes",
|
"TaggedNotes",
|
||||||
"TopicNotes"
|
"TopicNotes",
|
||||||
|
"Notebook"
|
||||||
);
|
);
|
||||||
setNotebooks();
|
setNotebooks();
|
||||||
SearchService.updateAndSearch();
|
SearchService.updateAndSearch();
|
||||||
@@ -420,9 +358,6 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
|
|
||||||
<SelectionProvider value={contextValue}>
|
<SelectionProvider value={contextValue}>
|
||||||
<FilteredList
|
<FilteredList
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
actionSheetRef.current?.handleChildScrollEnd();
|
|
||||||
}}
|
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 12
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
@@ -455,6 +390,7 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
itemState[item.id] === "deselected" &&
|
itemState[item.id] === "deselected" &&
|
||||||
getSelectedNotesCountInItem(item) > 0
|
getSelectedNotesCountInItem(item) > 0
|
||||||
}
|
}
|
||||||
|
sheetRef={actionSheetRef}
|
||||||
isSelected={itemState[item.id] === "selected"}
|
isSelected={itemState[item.id] === "selected"}
|
||||||
infoText={
|
infoText={
|
||||||
<>
|
<>
|
||||||
@@ -480,10 +416,6 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
if (currentState !== "selected") {
|
if (currentState !== "selected") {
|
||||||
resetItemState("deselected");
|
resetItemState("deselected");
|
||||||
contextValue.select(item);
|
contextValue.select(item);
|
||||||
updateItemState(
|
|
||||||
notebooks.find((n) => n.id === item.notebookId),
|
|
||||||
"intermediate"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
contextValue.deselect(item);
|
contextValue.deselect(item);
|
||||||
}
|
}
|
||||||
@@ -514,16 +446,24 @@ const MoveNoteComponent = ({ note }) => {
|
|||||||
onAddItem={async (title) => {
|
onAddItem={async (title) => {
|
||||||
return await onAddNotebook(title);
|
return await onAddNotebook(title);
|
||||||
}}
|
}}
|
||||||
ListFooterComponent={
|
// ListFooterComponent={
|
||||||
<View
|
// <View
|
||||||
style={{
|
// style={{
|
||||||
height: 200
|
// height: 200
|
||||||
}}
|
// }}
|
||||||
/>
|
// />
|
||||||
}
|
// }
|
||||||
/>
|
/>
|
||||||
</SelectionProvider>
|
</SelectionProvider>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MoveNoteSheet.present = (note) => {
|
||||||
|
presentSheet({
|
||||||
|
component: (ref) => <MoveNoteSheet actionSheetRef={ref} note={note} />,
|
||||||
|
enableGesturesInScrollView: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export default MoveNoteSheet;
|
||||||
|
|||||||
@@ -46,25 +46,15 @@ const _ListItem = ({
|
|||||||
removed,
|
removed,
|
||||||
isSelected,
|
isSelected,
|
||||||
hasHeaderSearch,
|
hasHeaderSearch,
|
||||||
onAddSublistItem
|
onAddSublistItem,
|
||||||
|
sheetRef
|
||||||
}) => {
|
}) => {
|
||||||
const { enabled, toggleSelection, setMultiSelect, select, deselect } =
|
const { enabled, toggleSelection, setMultiSelect } = useSelectionContext();
|
||||||
useSelectionContext();
|
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
function selectItem() {
|
function selectItem() {
|
||||||
const currentState = isSelected;
|
|
||||||
toggleSelection(item);
|
toggleSelection(item);
|
||||||
if (item.type === "notebook") {
|
|
||||||
item.topics.forEach((item) => {
|
|
||||||
if (currentState) {
|
|
||||||
deselect(item);
|
|
||||||
} else {
|
|
||||||
select(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -122,9 +112,6 @@ const _ListItem = ({
|
|||||||
: colors.icon
|
: colors.icon
|
||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (item.type === "notebook") {
|
|
||||||
setMultiSelect(true);
|
|
||||||
}
|
|
||||||
selectItem();
|
selectItem();
|
||||||
if (enabled) return;
|
if (enabled) return;
|
||||||
onPress?.(item);
|
onPress?.(item);
|
||||||
@@ -205,7 +192,7 @@ const _ListItem = ({
|
|||||||
style={{
|
style={{
|
||||||
width: "95%",
|
width: "95%",
|
||||||
alignSelf: "flex-end",
|
alignSelf: "flex-end",
|
||||||
maxHeight: 500
|
maxHeight: 250
|
||||||
}}
|
}}
|
||||||
itemType={sublistItemType}
|
itemType={sublistItemType}
|
||||||
hasHeaderSearch={hasHeaderSearch}
|
hasHeaderSearch={hasHeaderSearch}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
import React, { RefObject, useRef, useState } from "react";
|
import React, { RefObject, useRef, useState } from "react";
|
||||||
import { TextInput, View } from "react-native";
|
import { TextInput, View } from "react-native";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import {
|
||||||
eSendEvent,
|
eSendEvent,
|
||||||
@@ -31,7 +31,7 @@ import { Button } from "../../ui/button";
|
|||||||
import Input from "../../ui/input";
|
import Input from "../../ui/input";
|
||||||
|
|
||||||
type ChangeEmailProps = {
|
type ChangeEmailProps = {
|
||||||
actionSheetRef: RefObject<ActionSheet>;
|
actionSheetRef: RefObject<ActionSheetRef>;
|
||||||
close?: () => void;
|
close?: () => void;
|
||||||
update?: (options: PresentSheetOptions) => void;
|
update?: (options: PresentSheetOptions) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,51 +17,32 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { ScrollView, View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
import { ScrollView } from "react-native-actions-sheet";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||||
eSubscribeEvent,
|
|
||||||
eUnSubscribeEvent,
|
|
||||||
ToastEvent
|
|
||||||
} from "../../../services/event-manager";
|
|
||||||
import Navigation from "../../../services/navigation";
|
import Navigation from "../../../services/navigation";
|
||||||
import { useTagStore } from "../../../stores/use-tag-store";
|
import { useTagStore } from "../../../stores/use-tag-store";
|
||||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
import { eCloseTagsDialog, eOpenTagsDialog } from "../../../utils/events";
|
|
||||||
import { SIZE } from "../../../utils/size";
|
import { SIZE } from "../../../utils/size";
|
||||||
import { sleep } from "../../../utils/time";
|
|
||||||
import Input from "../../ui/input";
|
import Input from "../../ui/input";
|
||||||
import { PressableButton } from "../../ui/pressable";
|
import { PressableButton } from "../../ui/pressable";
|
||||||
import SheetWrapper from "../../ui/sheet";
|
|
||||||
import Heading from "../../ui/typography/heading";
|
import Heading from "../../ui/typography/heading";
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
import { useCallback } from "react";
|
const ManageTagsSheet = (props) => {
|
||||||
const ManageTagsSheet = () => {
|
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [visible, setVisible] = useState(false);
|
const [note, setNote] = useState(props.note);
|
||||||
const [note, setNote] = useState(null);
|
|
||||||
const allTags = useTagStore((state) => state.tags);
|
const allTags = useTagStore((state) => state.tags);
|
||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
const [query, setQuery] = useState(null);
|
const [query, setQuery] = useState(null);
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
const actionSheetRef = useRef();
|
const [focus, setFocus] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eSubscribeEvent(eOpenTagsDialog, open);
|
sortTags();
|
||||||
eSubscribeEvent(eCloseTagsDialog, close);
|
}, [allTags, note, query, sortTags]);
|
||||||
return () => {
|
|
||||||
eUnSubscribeEvent(eOpenTagsDialog, open);
|
|
||||||
eUnSubscribeEvent(eCloseTagsDialog, close);
|
|
||||||
};
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
sortTags();
|
|
||||||
}
|
|
||||||
}, [allTags, note, query, sortTags, visible]);
|
|
||||||
|
|
||||||
const sortTags = useCallback(() => {
|
const sortTags = useCallback(() => {
|
||||||
let _tags = [...allTags];
|
let _tags = [...allTags];
|
||||||
@@ -88,26 +69,9 @@ const ManageTagsSheet = () => {
|
|||||||
setTags(combinedTags);
|
setTags(combinedTags);
|
||||||
}, [allTags, note, query]);
|
}, [allTags, note, query]);
|
||||||
|
|
||||||
const open = useCallback(
|
|
||||||
(item) => {
|
|
||||||
setNote(item);
|
|
||||||
useTagStore.getState().setTags();
|
|
||||||
sortTags();
|
|
||||||
setVisible(true);
|
|
||||||
},
|
|
||||||
[sortTags]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
useTagStore.getState().setTags();
|
||||||
actionSheetRef.current?.show();
|
}, []);
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
setQuery(null);
|
|
||||||
actionSheetRef.current?.hide();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = async () => {
|
const onSubmit = async () => {
|
||||||
let _query = query;
|
let _query = query;
|
||||||
@@ -150,104 +114,96 @@ const ManageTagsSheet = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return !visible ? null : (
|
return (
|
||||||
<SheetWrapper
|
<View
|
||||||
centered={false}
|
style={{
|
||||||
fwdRef={actionSheetRef}
|
width: "100%",
|
||||||
onOpen={async () => {
|
alignSelf: "center",
|
||||||
await sleep(300);
|
paddingHorizontal: 12,
|
||||||
inputRef.current?.focus();
|
minHeight: focus ? "100%" : "60%"
|
||||||
}}
|
|
||||||
onClose={async () => {
|
|
||||||
setQuery(null);
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<Input
|
||||||
style={{
|
button={{
|
||||||
width: "100%",
|
icon: "magnify",
|
||||||
alignSelf: "center",
|
color: colors.accent,
|
||||||
paddingHorizontal: 12,
|
size: SIZE.lg
|
||||||
minHeight: "60%"
|
|
||||||
}}
|
}}
|
||||||
|
testID="tag-input"
|
||||||
|
fwdRef={inputRef}
|
||||||
|
autoCapitalize="none"
|
||||||
|
onChangeText={(v) => {
|
||||||
|
setQuery(db.tags.sanitize(v));
|
||||||
|
}}
|
||||||
|
onFocusInput={() => {
|
||||||
|
setFocus(true);
|
||||||
|
}}
|
||||||
|
onBlurInput={() => {
|
||||||
|
setFocus(false);
|
||||||
|
}}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
height={50}
|
||||||
|
placeholder="Search or add a tag"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
overScrollMode="never"
|
||||||
|
scrollToOverflowEnabled={false}
|
||||||
|
keyboardDismissMode="none"
|
||||||
|
keyboardShouldPersistTaps="always"
|
||||||
>
|
>
|
||||||
<Input
|
{query && query !== tags[0]?.title ? (
|
||||||
button={{
|
<PressableButton
|
||||||
icon: "magnify",
|
key={"query_item"}
|
||||||
color: colors.accent,
|
customStyle={{
|
||||||
size: SIZE.lg
|
flexDirection: "row",
|
||||||
}}
|
marginVertical: 5,
|
||||||
testID="tag-input"
|
justifyContent: "space-between",
|
||||||
fwdRef={inputRef}
|
padding: 12
|
||||||
autoCapitalize="none"
|
}}
|
||||||
onChangeText={(v) => {
|
onPress={onSubmit}
|
||||||
setQuery(db.tags.sanitize(v));
|
type="accent"
|
||||||
}}
|
>
|
||||||
onSubmit={onSubmit}
|
<Heading size={SIZE.sm} color={colors.light}>
|
||||||
height={50}
|
Add {'"' + "#" + query + '"'}
|
||||||
placeholder="Search or add a tag"
|
</Heading>
|
||||||
/>
|
<Icon name="plus" color={colors.light} size={SIZE.lg} />
|
||||||
|
</PressableButton>
|
||||||
|
) : null}
|
||||||
|
{!allTags || allTags.length === 0 ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: 200,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Heading size={50} color={colors.icon}>
|
||||||
|
#
|
||||||
|
</Heading>
|
||||||
|
<Paragraph textBreakStrategy="balanced" color={colors.icon}>
|
||||||
|
You do not have any tags.
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<ScrollView
|
{tags.map((item) => (
|
||||||
nestedScrollEnabled
|
<TagItem key={item.title} tag={item} note={note} setNote={setNote} />
|
||||||
overScrollMode="never"
|
))}
|
||||||
scrollToOverflowEnabled={false}
|
</ScrollView>
|
||||||
keyboardDismissMode="none"
|
</View>
|
||||||
keyboardShouldPersistTaps="always"
|
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
actionSheetRef.current?.handleChildScrollEnd();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{query && query !== tags[0]?.title ? (
|
|
||||||
<PressableButton
|
|
||||||
key={"query_item"}
|
|
||||||
customStyle={{
|
|
||||||
flexDirection: "row",
|
|
||||||
marginVertical: 5,
|
|
||||||
justifyContent: "space-between",
|
|
||||||
padding: 12
|
|
||||||
}}
|
|
||||||
onPress={onSubmit}
|
|
||||||
type="accent"
|
|
||||||
>
|
|
||||||
<Heading size={SIZE.sm} color={colors.light}>
|
|
||||||
Add {'"' + "#" + query + '"'}
|
|
||||||
</Heading>
|
|
||||||
<Icon name="plus" color={colors.light} size={SIZE.lg} />
|
|
||||||
</PressableButton>
|
|
||||||
) : null}
|
|
||||||
{!allTags || allTags.length === 0 ? (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: 200,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Heading size={50} color={colors.icon}>
|
|
||||||
#
|
|
||||||
</Heading>
|
|
||||||
<Paragraph textBreakStrategy="balanced" color={colors.icon}>
|
|
||||||
You do not have any tags.
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{tags.map((item) => (
|
|
||||||
<TagItem
|
|
||||||
key={item.title}
|
|
||||||
tag={item}
|
|
||||||
note={note}
|
|
||||||
setNote={setNote}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</SheetWrapper>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ManageTagsSheet.present = (note) => {
|
||||||
|
presentSheet({
|
||||||
|
component: (ref) => {
|
||||||
|
return <ManageTagsSheet actionSheetRef={ref} note={note} />;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default ManageTagsSheet;
|
export default ManageTagsSheet;
|
||||||
|
|
||||||
const TagItem = ({ tag, note, setNote }) => {
|
const TagItem = ({ tag, note, setNote }) => {
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FlashList } from "@shopify/flash-list";
|
|
||||||
import { NotebookType, NoteType, TopicType } from "app/utils/types";
|
import { NotebookType, NoteType, TopicType } from "app/utils/types";
|
||||||
import React, { RefObject, useState } from "react";
|
import React, { RefObject, useState } from "react";
|
||||||
import { Platform, useWindowDimensions, View } from "react-native";
|
import { Platform, useWindowDimensions, View } from "react-native";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||||
|
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import {
|
||||||
eSendEvent,
|
eSendEvent,
|
||||||
@@ -58,7 +58,7 @@ export const MoveNotes = ({
|
|||||||
}: {
|
}: {
|
||||||
notebook: NotebookType;
|
notebook: NotebookType;
|
||||||
selectedTopic?: TopicType;
|
selectedTopic?: TopicType;
|
||||||
fwdRef: RefObject<ActionSheet>;
|
fwdRef: RefObject<ActionSheetRef>;
|
||||||
}) => {
|
}) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [currentNotebook, setCurrentNotebook] = useState(notebook);
|
const [currentNotebook, setCurrentNotebook] = useState(notebook);
|
||||||
@@ -277,10 +277,6 @@ export const MoveNotes = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<FlashList
|
<FlashList
|
||||||
nestedScrollEnabled
|
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
fwdRef.current?.handleChildScrollEnd();
|
|
||||||
}}
|
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -345,7 +341,7 @@ export const MoveNotes = ({
|
|||||||
|
|
||||||
MoveNotes.present = (notebook: NotebookType, topic: TopicType) => {
|
MoveNotes.present = (notebook: NotebookType, topic: TopicType) => {
|
||||||
presentSheet({
|
presentSheet({
|
||||||
component: (ref: RefObject<ActionSheet>) => (
|
component: (ref: RefObject<ActionSheetRef>) => (
|
||||||
<MoveNotes fwdRef={ref} notebook={notebook} selectedTopic={topic} />
|
<MoveNotes fwdRef={ref} notebook={notebook} selectedTopic={topic} />
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,22 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Clipboard from "@react-native-clipboard/clipboard";
|
import Clipboard from "@react-native-clipboard/clipboard";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { ActivityIndicator, TouchableOpacity, View } from "react-native";
|
import { ActivityIndicator, TouchableOpacity, View } from "react-native";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||||
eSubscribeEvent,
|
|
||||||
eUnSubscribeEvent,
|
|
||||||
ToastEvent
|
|
||||||
} from "../../../services/event-manager";
|
|
||||||
import Navigation from "../../../services/navigation";
|
import Navigation from "../../../services/navigation";
|
||||||
import { useAttachmentStore } from "../../../stores/use-attachment-store";
|
import { useAttachmentStore } from "../../../stores/use-attachment-store";
|
||||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
import {
|
|
||||||
eClosePublishNoteDialog,
|
|
||||||
eOpenPublishNoteDialog
|
|
||||||
} from "../../../utils/events";
|
|
||||||
import { openLinkInBrowser } from "../../../utils/functions";
|
import { openLinkInBrowser } from "../../../utils/functions";
|
||||||
import { SIZE } from "../../../utils/size";
|
import { SIZE } from "../../../utils/size";
|
||||||
import DialogHeader from "../../dialog/dialog-header";
|
import DialogHeader from "../../dialog/dialog-header";
|
||||||
@@ -41,59 +33,27 @@ import { Button } from "../../ui/button";
|
|||||||
import { IconButton } from "../../ui/icon-button";
|
import { IconButton } from "../../ui/icon-button";
|
||||||
import Input from "../../ui/input";
|
import Input from "../../ui/input";
|
||||||
import Seperator from "../../ui/seperator";
|
import Seperator from "../../ui/seperator";
|
||||||
import SheetWrapper from "../../ui/sheet";
|
|
||||||
import Heading from "../../ui/typography/heading";
|
import Heading from "../../ui/typography/heading";
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
|
|
||||||
let passwordValue = null;
|
const PublishNoteSheet = ({ note: item, update }) => {
|
||||||
const PublishNoteSheet = () => {
|
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const actionSheetRef = useRef();
|
const actionSheetRef = useRef();
|
||||||
const loading = useAttachmentStore((state) => state.loading);
|
const loading = useAttachmentStore((state) => state.loading);
|
||||||
const [selfDestruct, setSelfDestruct] = useState(false);
|
const [selfDestruct, setSelfDestruct] = useState(false);
|
||||||
const [isLocked, setIsLocked] = useState(false);
|
const [isLocked, setIsLocked] = useState(false);
|
||||||
const [note, setNote] = useState(null);
|
const [note, setNote] = useState(item);
|
||||||
const [publishing, setPublishing] = useState(false);
|
const [publishing, setPublishing] = useState(false);
|
||||||
const publishUrl =
|
const publishUrl =
|
||||||
note &&
|
note &&
|
||||||
`https://monograph.notesnook.com/${db?.monographs.monograph(note?.id)}`;
|
`https://monograph.notesnook.com/${db?.monographs.monograph(note?.id)}`;
|
||||||
const isPublished = note && db?.monographs.isPublished(note?.id);
|
const isPublished = note && db?.monographs.isPublished(note?.id);
|
||||||
const pwdInput = useRef();
|
const pwdInput = useRef();
|
||||||
|
const passwordValue = useRef();
|
||||||
useEffect(() => {
|
|
||||||
eSubscribeEvent(eOpenPublishNoteDialog, open);
|
|
||||||
eSubscribeEvent(eClosePublishNoteDialog, close);
|
|
||||||
return () => {
|
|
||||||
eUnSubscribeEvent(eOpenPublishNoteDialog, open);
|
|
||||||
eUnSubscribeEvent(eClosePublishNoteDialog, close);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const open = (item) => {
|
|
||||||
if (!item) return;
|
|
||||||
setNote(item);
|
|
||||||
setPublishing(false);
|
|
||||||
setSelfDestruct(false);
|
|
||||||
setIsLocked(false);
|
|
||||||
setVisible(true);
|
|
||||||
passwordValue = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
actionSheetRef.current?.show();
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
passwordValue = null;
|
|
||||||
actionSheetRef.current?.hide();
|
|
||||||
};
|
|
||||||
|
|
||||||
const publishNote = async () => {
|
const publishNote = async () => {
|
||||||
if (publishing) return;
|
if (publishing) return;
|
||||||
setPublishing(true);
|
setPublishLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (note?.id) {
|
if (note?.id) {
|
||||||
@@ -110,6 +70,7 @@ const PublishNoteSheet = () => {
|
|||||||
"TaggedNotes",
|
"TaggedNotes",
|
||||||
"TopicNotes"
|
"TopicNotes"
|
||||||
);
|
);
|
||||||
|
setPublishLoading(false);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
@@ -120,12 +81,19 @@ const PublishNoteSheet = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setPublishing(false);
|
setPublishLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPublishLoading = (value) => {
|
||||||
|
setPublishing(value);
|
||||||
|
update({
|
||||||
|
progress: value
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletePublishedNote = async () => {
|
const deletePublishedNote = async () => {
|
||||||
if (publishing) return;
|
if (publishing) return;
|
||||||
setPublishing(true);
|
setPublishLoading(true);
|
||||||
try {
|
try {
|
||||||
if (note?.id) {
|
if (note?.id) {
|
||||||
await db.monographs.unpublish(note.id);
|
await db.monographs.unpublish(note.id);
|
||||||
@@ -137,6 +105,7 @@ const PublishNoteSheet = () => {
|
|||||||
"TaggedNotes",
|
"TaggedNotes",
|
||||||
"TopicNotes"
|
"TopicNotes"
|
||||||
);
|
);
|
||||||
|
setPublishLoading(false);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ToastEvent.show({
|
ToastEvent.show({
|
||||||
@@ -147,267 +116,268 @@ const PublishNoteSheet = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
actionSheetRef.current?.hide();
|
actionSheetRef.current?.hide();
|
||||||
setPublishing(false);
|
setPublishLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return !visible ? null : (
|
return (
|
||||||
<SheetWrapper
|
<View
|
||||||
centered={false}
|
style={{
|
||||||
fwdRef={actionSheetRef}
|
width: "100%",
|
||||||
closeOnTouchBackdrop={!publishing}
|
alignSelf: "center",
|
||||||
gestureEnabled={!publishing}
|
paddingHorizontal: 12
|
||||||
onClose={async () => {
|
|
||||||
passwordValue = null;
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<DialogHeader
|
||||||
style={{
|
title={note?.title}
|
||||||
width: "100%",
|
paragraph={`Anyone with the link${
|
||||||
alignSelf: "center",
|
isLocked ? " and password" : ""
|
||||||
paddingHorizontal: 12
|
} of the published note can view it.`}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
<DialogHeader
|
|
||||||
title={note?.title}
|
|
||||||
paragraph={`Anyone with the link${
|
|
||||||
isLocked ? " and password" : ""
|
|
||||||
} of the published note can view it.`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{publishing ? (
|
{publishing ? (
|
||||||
<View
|
<View
|
||||||
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
alignContent: "center",
|
||||||
|
height: 150,
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ActivityIndicator size={25} color={colors.accent} />
|
||||||
|
<Paragraph
|
||||||
style={{
|
style={{
|
||||||
justifyContent: "center",
|
textAlign: "center"
|
||||||
alignContent: "center",
|
|
||||||
height: 150,
|
|
||||||
width: "100%"
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ActivityIndicator size={25} color={colors.accent} />
|
Please wait...
|
||||||
<Paragraph
|
{loading && loading.current && loading.total
|
||||||
|
? `\nDownloading attachments (${
|
||||||
|
loading?.current / loading?.total
|
||||||
|
})`
|
||||||
|
: ""}
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{isPublished && (
|
||||||
|
<View
|
||||||
style={{
|
style={{
|
||||||
textAlign: "center"
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: 15,
|
||||||
|
backgroundColor: colors.nav,
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Please wait...
|
|
||||||
{loading && loading.current && loading.total
|
|
||||||
? `\nDownloading attachments (${
|
|
||||||
loading?.current / loading?.total
|
|
||||||
})`
|
|
||||||
: ""}
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{isPublished && (
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: "row",
|
width: "100%",
|
||||||
alignItems: "center",
|
flexShrink: 1
|
||||||
marginTop: 15,
|
|
||||||
backgroundColor: colors.nav,
|
|
||||||
padding: 12,
|
|
||||||
borderRadius: 5
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<Heading size={SIZE.sm}>Published at:</Heading>
|
||||||
|
<Paragraph size={SIZE.xs} numberOfLines={1}>
|
||||||
|
{publishUrl}
|
||||||
|
</Paragraph>
|
||||||
|
<Paragraph
|
||||||
|
onPress={async () => {
|
||||||
|
try {
|
||||||
|
await openLinkInBrowser(publishUrl, colors.accent);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size={SIZE.xs}
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
marginTop: 5,
|
||||||
flexShrink: 1
|
color: colors.pri
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Heading size={SIZE.sm}>Published at:</Heading>
|
<Icon color={colors.accent} name="open-in-new" /> Open in
|
||||||
<Paragraph size={SIZE.xs} numberOfLines={1}>
|
browser
|
||||||
{publishUrl}
|
</Paragraph>
|
||||||
</Paragraph>
|
|
||||||
<Paragraph
|
|
||||||
onPress={async () => {
|
|
||||||
try {
|
|
||||||
await openLinkInBrowser(publishUrl, colors.accent);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
size={SIZE.xs}
|
|
||||||
style={{
|
|
||||||
marginTop: 5,
|
|
||||||
color: colors.pri
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon color={colors.accent} name="open-in-new" /> Open in
|
|
||||||
browser
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
onPress={() => {
|
|
||||||
Clipboard.setString(publishUrl);
|
|
||||||
ToastEvent.show({
|
|
||||||
heading: "Note publish url copied",
|
|
||||||
type: "success",
|
|
||||||
context: "local"
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
color={colors.accent}
|
|
||||||
size={SIZE.lg}
|
|
||||||
name="content-copy"
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
|
||||||
<Seperator />
|
|
||||||
|
|
||||||
<TouchableOpacity
|
<IconButton
|
||||||
|
onPress={() => {
|
||||||
|
Clipboard.setString(publishUrl);
|
||||||
|
ToastEvent.show({
|
||||||
|
heading: "Note publish url copied",
|
||||||
|
type: "success",
|
||||||
|
context: "local"
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color={colors.accent}
|
||||||
|
size={SIZE.lg}
|
||||||
|
name="content-copy"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<Seperator />
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
if (publishing) return;
|
||||||
|
setIsLocked(!isLocked);
|
||||||
|
}}
|
||||||
|
activeOpacity={0.9}
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (publishing) return;
|
if (publishing) return;
|
||||||
setIsLocked(!isLocked);
|
setIsLocked(!isLocked);
|
||||||
}}
|
}}
|
||||||
activeOpacity={0.9}
|
color={isLocked ? colors.accent : colors.icon}
|
||||||
style={{
|
size={SIZE.lg}
|
||||||
flexDirection: "row",
|
name={
|
||||||
alignItems: "center",
|
isLocked
|
||||||
marginBottom: 10
|
? "check-circle-outline"
|
||||||
}}
|
: "checkbox-blank-circle-outline"
|
||||||
>
|
}
|
||||||
<IconButton
|
/>
|
||||||
onPress={() => {
|
|
||||||
if (publishing) return;
|
|
||||||
setIsLocked(!isLocked);
|
|
||||||
}}
|
|
||||||
color={isLocked ? colors.accent : colors.icon}
|
|
||||||
size={SIZE.lg}
|
|
||||||
name={
|
|
||||||
isLocked
|
|
||||||
? "check-circle-outline"
|
|
||||||
: "checkbox-blank-circle-outline"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
flexShrink: 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Heading size={SIZE.md}>Password protection</Heading>
|
|
||||||
<Paragraph>
|
|
||||||
Published note can only be viewed by someone with the
|
|
||||||
password.
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => {
|
|
||||||
setSelfDestruct(!selfDestruct);
|
|
||||||
}}
|
|
||||||
activeOpacity={0.9}
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
onPress={() => {
|
|
||||||
setSelfDestruct(!selfDestruct);
|
|
||||||
}}
|
|
||||||
color={selfDestruct ? colors.accent : colors.icon}
|
|
||||||
size={SIZE.lg}
|
|
||||||
name={
|
|
||||||
selfDestruct
|
|
||||||
? "check-circle-outline"
|
|
||||||
: "checkbox-blank-circle-outline"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
flexShrink: 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Heading size={SIZE.md}>Self destruct</Heading>
|
|
||||||
<Paragraph>
|
|
||||||
Published note link will be automatically deleted once it is
|
|
||||||
viewed by someone.
|
|
||||||
</Paragraph>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
alignSelf: "center",
|
flexShrink: 1
|
||||||
marginTop: 10
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLocked ? (
|
<Heading size={SIZE.md}>Password protection</Heading>
|
||||||
<>
|
<Paragraph>
|
||||||
<Input
|
Published note can only be viewed by someone with the password.
|
||||||
fwdRef={pwdInput}
|
</Paragraph>
|
||||||
onChangeText={(value) => (passwordValue = value)}
|
|
||||||
blurOnSubmit
|
|
||||||
secureTextEntry
|
|
||||||
defaultValue={passwordValue}
|
|
||||||
placeholder="Enter Password"
|
|
||||||
/>
|
|
||||||
<Seperator half />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onPress={publishNote}
|
|
||||||
fontSize={SIZE.md}
|
|
||||||
width="100%"
|
|
||||||
style={{
|
|
||||||
marginTop: 10
|
|
||||||
}}
|
|
||||||
height={50}
|
|
||||||
type="accent"
|
|
||||||
title={isPublished ? "Update published note" : "Publish note"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isPublished && (
|
|
||||||
<>
|
|
||||||
<Seperator half />
|
|
||||||
<Button
|
|
||||||
onPress={deletePublishedNote}
|
|
||||||
fontSize={SIZE.md}
|
|
||||||
width="100%"
|
|
||||||
height={50}
|
|
||||||
type="error"
|
|
||||||
title="Unpublish note"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</>
|
</TouchableOpacity>
|
||||||
)}
|
|
||||||
|
|
||||||
<Paragraph
|
<TouchableOpacity
|
||||||
color={colors.icon}
|
onPress={() => {
|
||||||
size={SIZE.xs}
|
setSelfDestruct(!selfDestruct);
|
||||||
style={{
|
}}
|
||||||
textAlign: "center",
|
activeOpacity={0.9}
|
||||||
marginTop: 5,
|
style={{
|
||||||
textDecorationLine: "underline"
|
flexDirection: "row",
|
||||||
}}
|
alignItems: "center"
|
||||||
onPress={async () => {
|
}}
|
||||||
try {
|
>
|
||||||
await openLinkInBrowser(
|
<IconButton
|
||||||
"https://docs.notesnook.com/monographs/",
|
onPress={() => {
|
||||||
colors.accent
|
setSelfDestruct(!selfDestruct);
|
||||||
);
|
}}
|
||||||
} catch (e) {
|
color={selfDestruct ? colors.accent : colors.icon}
|
||||||
console.error(e);
|
size={SIZE.lg}
|
||||||
}
|
name={
|
||||||
}}
|
selfDestruct
|
||||||
>
|
? "check-circle-outline"
|
||||||
Learn more about Notesnook Monograph
|
: "checkbox-blank-circle-outline"
|
||||||
</Paragraph>
|
}
|
||||||
</View>
|
/>
|
||||||
</SheetWrapper>
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
flexShrink: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Heading size={SIZE.md}>Self destruct</Heading>
|
||||||
|
<Paragraph>
|
||||||
|
Published note link will be automatically deleted once it is
|
||||||
|
viewed by someone.
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
alignSelf: "center",
|
||||||
|
marginTop: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isLocked ? (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
fwdRef={pwdInput}
|
||||||
|
onChangeText={(value) => (passwordValue.current = value)}
|
||||||
|
blurOnSubmit
|
||||||
|
secureTextEntry
|
||||||
|
defaultValue={passwordValue.current}
|
||||||
|
placeholder="Enter Password"
|
||||||
|
/>
|
||||||
|
<Seperator half />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onPress={publishNote}
|
||||||
|
fontSize={SIZE.md}
|
||||||
|
width="100%"
|
||||||
|
style={{
|
||||||
|
marginTop: 10
|
||||||
|
}}
|
||||||
|
height={50}
|
||||||
|
type="accent"
|
||||||
|
title={isPublished ? "Update published note" : "Publish note"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isPublished && (
|
||||||
|
<>
|
||||||
|
<Seperator half />
|
||||||
|
<Button
|
||||||
|
onPress={deletePublishedNote}
|
||||||
|
fontSize={SIZE.md}
|
||||||
|
width="100%"
|
||||||
|
height={50}
|
||||||
|
type="error"
|
||||||
|
title="Unpublish note"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
color={colors.icon}
|
||||||
|
size={SIZE.xs}
|
||||||
|
style={{
|
||||||
|
textAlign: "center",
|
||||||
|
marginTop: 5,
|
||||||
|
textDecorationLine: "underline"
|
||||||
|
}}
|
||||||
|
onPress={async () => {
|
||||||
|
try {
|
||||||
|
await openLinkInBrowser(
|
||||||
|
"https://docs.notesnook.com/monographs/",
|
||||||
|
colors.accent
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Learn more about Notesnook Monograph
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PublishNoteSheet.present = (note) => {
|
||||||
|
presentSheet({
|
||||||
|
component: (ref, close, update) => (
|
||||||
|
<PublishNoteSheet
|
||||||
|
actionSheetRef={ref}
|
||||||
|
close={close}
|
||||||
|
update={update}
|
||||||
|
note={note}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default PublishNoteSheet;
|
export default PublishNoteSheet;
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
import React, { RefObject, useEffect, useState } from "react";
|
import React, { RefObject, useEffect, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||||
|
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import {
|
||||||
PresentSheetOptions,
|
presentSheet,
|
||||||
presentSheet
|
PresentSheetOptions
|
||||||
} from "../../../services/event-manager";
|
} from "../../../services/event-manager";
|
||||||
import { Reminder } from "../../../services/notifications";
|
import { Reminder } from "../../../services/notifications";
|
||||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||||
@@ -37,7 +38,7 @@ import { PressableButtonProps } from "../../ui/pressable";
|
|||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
|
|
||||||
type RelationsListProps = {
|
type RelationsListProps = {
|
||||||
actionSheetRef: RefObject<ActionSheet>;
|
actionSheetRef: RefObject<ActionSheetRef>;
|
||||||
close?: () => void;
|
close?: () => void;
|
||||||
update?: (options: PresentSheetOptions) => void;
|
update?: (options: PresentSheetOptions) => void;
|
||||||
item: { id: string; type: string };
|
item: { id: string; type: string };
|
||||||
@@ -62,8 +63,6 @@ const IconsByType = {
|
|||||||
|
|
||||||
export const RelationsList = ({
|
export const RelationsList = ({
|
||||||
actionSheetRef,
|
actionSheetRef,
|
||||||
close,
|
|
||||||
update,
|
|
||||||
item,
|
item,
|
||||||
referenceType,
|
referenceType,
|
||||||
relationType,
|
relationType,
|
||||||
@@ -75,7 +74,6 @@ export const RelationsList = ({
|
|||||||
const [items, setItems] = useState<Reminder[]>([]);
|
const [items, setItems] = useState<Reminder[]>([]);
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const hasNoRelations = !items || items.length === 0;
|
const hasNoRelations = !items || items.length === 0;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setItems(
|
setItems(
|
||||||
db.relations?.[relationType]?.(
|
db.relations?.[relationType]?.(
|
||||||
@@ -123,6 +121,7 @@ export const RelationsList = ({
|
|||||||
) : (
|
) : (
|
||||||
<List
|
<List
|
||||||
listData={items}
|
listData={items}
|
||||||
|
ScrollComponent={FlashList}
|
||||||
loading={false}
|
loading={false}
|
||||||
type={referenceType}
|
type={referenceType}
|
||||||
headerProps={null}
|
headerProps={null}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import React, { RefObject } from "react";
|
import React, { RefObject } from "react";
|
||||||
import { ScrollView, View } from "react-native";
|
import { View } from "react-native";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
|
||||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import {
|
import {
|
||||||
@@ -36,7 +36,7 @@ import Heading from "../../ui/typography/heading";
|
|||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
|
|
||||||
type ReminderSheetProps = {
|
type ReminderSheetProps = {
|
||||||
actionSheetRef: RefObject<ActionSheet>;
|
actionSheetRef: RefObject<ActionSheetRef>;
|
||||||
close?: () => void;
|
close?: () => void;
|
||||||
update?: (options: PresentSheetOptions) => void;
|
update?: (options: PresentSheetOptions) => void;
|
||||||
reminder?: Reminder;
|
reminder?: Reminder;
|
||||||
|
|||||||
@@ -17,14 +17,8 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import React, { RefObject, useRef, useState } from "react";
|
import React, { RefObject, useRef, useState } from "react";
|
||||||
import {
|
import { Platform, TextInput, View } from "react-native";
|
||||||
Platform,
|
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
|
||||||
ScrollView,
|
|
||||||
TextInput,
|
|
||||||
useWindowDimensions,
|
|
||||||
View
|
|
||||||
} from "react-native";
|
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
|
||||||
import DateTimePickerModal from "react-native-modal-datetime-picker";
|
import DateTimePickerModal from "react-native-modal-datetime-picker";
|
||||||
import {
|
import {
|
||||||
presentSheet,
|
presentSheet,
|
||||||
@@ -44,13 +38,13 @@ import Notifications, { Reminder } from "../../../services/notifications";
|
|||||||
import PremiumService from "../../../services/premium";
|
import PremiumService from "../../../services/premium";
|
||||||
import SettingsService from "../../../services/settings";
|
import SettingsService from "../../../services/settings";
|
||||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||||
import { ReminderTime } from "../../ui/reminder-time";
|
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
|
||||||
import { NoteType } from "../../../utils/types";
|
import { NoteType } from "../../../utils/types";
|
||||||
import { Dialog } from "../../dialog";
|
import { Dialog } from "../../dialog";
|
||||||
|
import { ReminderTime } from "../../ui/reminder-time";
|
||||||
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
|
|
||||||
type ReminderSheetProps = {
|
type ReminderSheetProps = {
|
||||||
actionSheetRef: RefObject<ActionSheet>;
|
actionSheetRef: RefObject<ActionSheetRef>;
|
||||||
close?: (ctx?: string) => void;
|
close?: (ctx?: string) => void;
|
||||||
update?: (options: PresentSheetOptions) => void;
|
update?: (options: PresentSheetOptions) => void;
|
||||||
reminder?: Reminder;
|
reminder?: Reminder;
|
||||||
@@ -117,13 +111,15 @@ export default function ReminderSheet({
|
|||||||
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
|
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
|
||||||
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
|
||||||
const [repeatFrequency, setRepeatFrequency] = useState(1);
|
const [repeatFrequency, setRepeatFrequency] = useState(1);
|
||||||
const title = useRef<string | undefined>(reminder?.title);
|
|
||||||
const details = useRef<string | undefined>(reminder?.description);
|
|
||||||
const titleRef = useRef<TextInput>(null);
|
|
||||||
const { height } = useWindowDimensions();
|
|
||||||
const referencedItem = reference
|
const referencedItem = reference
|
||||||
? (db.notes?.note(reference.id)?.data as NoteType)
|
? (db.notes?.note(reference.id)?.data as NoteType)
|
||||||
: null;
|
: null;
|
||||||
|
const title = useRef<string | undefined>(
|
||||||
|
reminder?.title || referencedItem?.title
|
||||||
|
);
|
||||||
|
const details = useRef<string | undefined>(reminder?.description);
|
||||||
|
const titleRef = useRef<TextInput>(null);
|
||||||
|
const timer = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
const showDatePicker = () => {
|
const showDatePicker = () => {
|
||||||
setDatePickerVisibility(true);
|
setDatePickerVisibility(true);
|
||||||
@@ -134,9 +130,10 @@ export default function ReminderSheet({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirm = (date: Date) => {
|
const handleConfirm = (date: Date) => {
|
||||||
hideDatePicker();
|
timer.current = setTimeout(() => {
|
||||||
setDate(date);
|
hideDatePicker();
|
||||||
console.log(date);
|
setDate(date);
|
||||||
|
}, 50);
|
||||||
};
|
};
|
||||||
function nth(n: number) {
|
function nth(n: number) {
|
||||||
return (
|
return (
|
||||||
@@ -233,13 +230,8 @@ export default function ReminderSheet({
|
|||||||
paddingHorizontal: 12
|
paddingHorizontal: 12
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog context="local"/>
|
<Dialog context="local" />
|
||||||
<ScrollView
|
<ScrollView keyboardShouldPersistTaps="always">
|
||||||
onScrollEndDrag={() => actionSheetRef.current?.handleChildScrollEnd()}
|
|
||||||
style={{
|
|
||||||
maxHeight: height * 0.85
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
<Input
|
||||||
fwdRef={titleRef}
|
fwdRef={titleRef}
|
||||||
defaultValue={reminder?.title || referencedItem?.title}
|
defaultValue={reminder?.title || referencedItem?.title}
|
||||||
@@ -249,7 +241,9 @@ export default function ReminderSheet({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
defaultValue={reminder ? reminder?.description : referencedItem?.headline}
|
defaultValue={
|
||||||
|
reminder ? reminder?.description : referencedItem?.headline
|
||||||
|
}
|
||||||
placeholder="Add a quick note"
|
placeholder="Add a quick note"
|
||||||
onChangeText={(text) => (details.current = text)}
|
onChangeText={(text) => (details.current = text)}
|
||||||
containerStyle={{
|
containerStyle={{
|
||||||
@@ -440,6 +434,12 @@ export default function ReminderSheet({
|
|||||||
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
date={date}
|
date={date}
|
||||||
|
minimumDate={
|
||||||
|
dayjs(date).subtract(3, "months").isBefore(dayjs())
|
||||||
|
? dayjs().toDate()
|
||||||
|
: dayjs(date).subtract(3, "months").toDate()
|
||||||
|
}
|
||||||
|
maximumDate={dayjs(date).add(3, "months").toDate()}
|
||||||
onDateChange={handleConfirm}
|
onDateChange={handleConfirm}
|
||||||
textColor={colors.night ? "#ffffff" : "#000000"}
|
textColor={colors.night ? "#ffffff" : "#000000"}
|
||||||
fadeToColor={colors.bg}
|
fadeToColor={colors.bg}
|
||||||
@@ -561,16 +561,16 @@ export default function ReminderSheet({
|
|||||||
alignSelf: "flex-start"
|
alignSelf: "flex-start"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
title="Save"
|
||||||
|
type="accent"
|
||||||
|
fontSize={SIZE.md}
|
||||||
|
onPress={saveReminder}
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<Button
|
|
||||||
style={{
|
|
||||||
width: "100%"
|
|
||||||
}}
|
|
||||||
title="Save"
|
|
||||||
type="accent"
|
|
||||||
fontSize={SIZE.md}
|
|
||||||
onPress={saveReminder}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -582,6 +582,7 @@ ReminderSheet.present = (
|
|||||||
) => {
|
) => {
|
||||||
presentSheet({
|
presentSheet({
|
||||||
context: isSheet ? "local" : undefined,
|
context: isSheet ? "local" : undefined,
|
||||||
|
enableGesturesInScrollView: true,
|
||||||
component: (ref, close, update) => (
|
component: (ref, close, update) => (
|
||||||
<ReminderSheet
|
<ReminderSheet
|
||||||
actionSheetRef={ref}
|
actionSheetRef={ref}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { EVENTS } from "@notesnook/core/common";
|
import { EVENTS } from "@notesnook/core/common";
|
||||||
import React, { createRef, useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { ActivityIndicator, Platform, View } from "react-native";
|
import { ActivityIndicator, Platform, View } from "react-native";
|
||||||
|
import { FlatList } from "react-native-actions-sheet";
|
||||||
import DocumentPicker from "react-native-document-picker";
|
import DocumentPicker from "react-native-document-picker";
|
||||||
import { FlatList } from "react-native-gesture-handler";
|
|
||||||
import * as ScopedStorage from "react-native-scoped-storage";
|
import * as ScopedStorage from "react-native-scoped-storage";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import storage from "../../../common/database/storage";
|
import storage from "../../../common/database/storage";
|
||||||
@@ -35,7 +35,7 @@ import { initialize } from "../../../stores";
|
|||||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events";
|
import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events";
|
||||||
import { SIZE } from "../../../utils/size";
|
import { SIZE } from "../../../utils/size";
|
||||||
import { sleep, timeConverter } from "../../../utils/time";
|
import { timeConverter } from "../../../utils/time";
|
||||||
import { Dialog } from "../../dialog";
|
import { Dialog } from "../../dialog";
|
||||||
import DialogHeader from "../../dialog/dialog-header";
|
import DialogHeader from "../../dialog/dialog-header";
|
||||||
import { presentDialog } from "../../dialog/functions";
|
import { presentDialog } from "../../dialog/functions";
|
||||||
@@ -44,12 +44,18 @@ import { Button } from "../../ui/button";
|
|||||||
import Seperator from "../../ui/seperator";
|
import Seperator from "../../ui/seperator";
|
||||||
import SheetWrapper from "../../ui/sheet";
|
import SheetWrapper from "../../ui/sheet";
|
||||||
import Paragraph from "../../ui/typography/paragraph";
|
import Paragraph from "../../ui/typography/paragraph";
|
||||||
const actionSheetRef = createRef();
|
|
||||||
let RNFetchBlob;
|
let RNFetchBlob;
|
||||||
const RestoreDataSheet = () => {
|
const RestoreDataSheet = () => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [restoring, setRestoring] = useState(false);
|
const [restoring, setRestoring] = useState(false);
|
||||||
|
const sheet = useRef();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const open = async () => {
|
||||||
|
setVisible(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
sheet.current?.show();
|
||||||
|
}, 1);
|
||||||
|
};
|
||||||
eSubscribeEvent(eOpenRestoreDialog, open);
|
eSubscribeEvent(eOpenRestoreDialog, open);
|
||||||
eSubscribeEvent(eCloseRestoreDialog, close);
|
eSubscribeEvent(eCloseRestoreDialog, close);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -58,21 +64,15 @@ const RestoreDataSheet = () => {
|
|||||||
};
|
};
|
||||||
}, [close]);
|
}, [close]);
|
||||||
|
|
||||||
const open = async () => {
|
|
||||||
setVisible(true);
|
|
||||||
await sleep(30);
|
|
||||||
actionSheetRef.current?.setModalVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = useCallback(() => {
|
const close = useCallback(() => {
|
||||||
if (restoring) {
|
if (restoring) {
|
||||||
showIsWorking();
|
showIsWorking();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
actionSheetRef.current?.setModalVisible(false);
|
sheet.current?.hide();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}, 300);
|
}, 150);
|
||||||
}, [restoring]);
|
}, [restoring]);
|
||||||
|
|
||||||
const showIsWorking = () => {
|
const showIsWorking = () => {
|
||||||
@@ -86,15 +86,19 @@ const RestoreDataSheet = () => {
|
|||||||
|
|
||||||
return !visible ? null : (
|
return !visible ? null : (
|
||||||
<SheetWrapper
|
<SheetWrapper
|
||||||
fwdRef={actionSheetRef}
|
fwdRef={sheet}
|
||||||
gestureEnabled={!restoring}
|
gestureEnabled={!restoring}
|
||||||
closeOnTouchBackdrop={!restoring}
|
closeOnTouchBackdrop={!restoring}
|
||||||
onClose={close}
|
onClose={() => {
|
||||||
|
setVisible(false);
|
||||||
|
close();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<RestoreDataComponent
|
<RestoreDataComponent
|
||||||
close={close}
|
close={close}
|
||||||
restoring={restoring}
|
restoring={restoring}
|
||||||
setRestoring={setRestoring}
|
setRestoring={setRestoring}
|
||||||
|
actionSheetRef={sheet}
|
||||||
/>
|
/>
|
||||||
<Toast context="local" />
|
<Toast context="local" />
|
||||||
</SheetWrapper>
|
</SheetWrapper>
|
||||||
@@ -109,7 +113,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [backupDirectoryAndroid, setBackupDirectoryAndroid] = useState(false);
|
const [backupDirectoryAndroid, setBackupDirectoryAndroid] = useState(false);
|
||||||
const [progress, setProgress] = useState();
|
const [progress, setProgress] = useState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = db.eventManager.subscribe(
|
const subscription = db.eventManager.subscribe(
|
||||||
EVENTS.migrationProgress,
|
EVENTS.migrationProgress,
|
||||||
@@ -123,7 +126,9 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkBackups();
|
setTimeout(() => {
|
||||||
|
checkBackups();
|
||||||
|
}, 300);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const restore = async (item) => {
|
const restore = async (item) => {
|
||||||
@@ -362,10 +367,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
|||||||
</View>
|
</View>
|
||||||
<Seperator half />
|
<Seperator half />
|
||||||
<FlatList
|
<FlatList
|
||||||
nestedScrollEnabled
|
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
actionSheetRef.current?.handleChildScrollEnd();
|
|
||||||
}}
|
|
||||||
ListEmptyComponent={
|
ListEmptyComponent={
|
||||||
!restoring ? (
|
!restoring ? (
|
||||||
loading ? (
|
loading ? (
|
||||||
|
|||||||
423
apps/mobile/app/components/sheets/topic-sheet/index.tsx
Normal file
423
apps/mobile/app/components/sheets/topic-sheet/index.tsx
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
/*
|
||||||
|
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 qclone from "qclone";
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from "react";
|
||||||
|
import { Animated, Dimensions, View, RefreshControl } from "react-native";
|
||||||
|
import ActionSheet, {
|
||||||
|
ActionSheetRef,
|
||||||
|
FlatList
|
||||||
|
} from "react-native-actions-sheet";
|
||||||
|
import { db } from "../../../common/database";
|
||||||
|
import { IconButton } from "../../../components/ui/icon-button";
|
||||||
|
import { PressableButton } from "../../../components/ui/pressable";
|
||||||
|
import Paragraph from "../../../components/ui/typography/paragraph";
|
||||||
|
import { TopicNotes } from "../../../screens/notes/topic-notes";
|
||||||
|
import {
|
||||||
|
eSendEvent,
|
||||||
|
eSubscribeEvent,
|
||||||
|
eUnSubscribeEvent
|
||||||
|
} from "../../../services/event-manager";
|
||||||
|
import useNavigationStore, {
|
||||||
|
NotebookScreenParams
|
||||||
|
} from "../../../stores/use-navigation-store";
|
||||||
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
|
import { eOnNewTopicAdded, eOpenAddTopicDialog } from "../../../utils/events";
|
||||||
|
import { normalize, SIZE } from "../../../utils/size";
|
||||||
|
import { NotebookType, TopicType } from "../../../utils/types";
|
||||||
|
|
||||||
|
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||||
|
import { openEditor } from "../../../screens/notes/common";
|
||||||
|
import { getTotalNotes, history } from "../../../utils";
|
||||||
|
import { Properties } from "../../properties";
|
||||||
|
import { deleteItems } from "../../../utils/functions";
|
||||||
|
import { presentDialog } from "../../dialog/functions";
|
||||||
|
export const TopicsSheet = () => {
|
||||||
|
const currentScreen = useNavigationStore((state) => state.currentScreen);
|
||||||
|
const canShow =
|
||||||
|
currentScreen.name === "Notebook" || currentScreen.name === "TopicNotes";
|
||||||
|
const [notebook, setNotebook] = useState(
|
||||||
|
canShow
|
||||||
|
? db.notebooks?.notebook(
|
||||||
|
currentScreen?.notebookId || currentScreen?.id || ""
|
||||||
|
)?.data
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
const [selection, setSelection] = useState<TopicType[]>([]);
|
||||||
|
const [enabled, setEnabled] = useState(false);
|
||||||
|
const colors = useThemeStore((state) => state.colors);
|
||||||
|
const ref = useRef<ActionSheetRef>(null);
|
||||||
|
const [topics, setTopics] = useState(notebook ? qclone(notebook.topics) : []);
|
||||||
|
const [animations] = useState({
|
||||||
|
translate: new Animated.Value(0),
|
||||||
|
display: new Animated.Value(-5000),
|
||||||
|
opacity: new Animated.Value(0)
|
||||||
|
});
|
||||||
|
const onRequestUpdate = React.useCallback(
|
||||||
|
(data?: NotebookScreenParams) => {
|
||||||
|
if (!canShow) return;
|
||||||
|
if (!data) data = { item: notebook } as NotebookScreenParams;
|
||||||
|
const _notebook = db.notebooks?.notebook(data.item?.id)
|
||||||
|
?.data as NotebookType;
|
||||||
|
if (_notebook) {
|
||||||
|
setNotebook(_notebook);
|
||||||
|
setTopics(qclone(_notebook.topics));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[notebook, canShow]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
eSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
|
||||||
|
return () => {
|
||||||
|
eUnSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
|
||||||
|
};
|
||||||
|
}, [onRequestUpdate]);
|
||||||
|
|
||||||
|
const PLACEHOLDER_DATA = {
|
||||||
|
heading: "Topics",
|
||||||
|
paragraph: "You have not added any topics yet.",
|
||||||
|
button: "Add first topic",
|
||||||
|
action: () => {
|
||||||
|
eSendEvent(eOpenAddTopicDialog, { notebookId: notebook.id });
|
||||||
|
},
|
||||||
|
loading: "Loading notebook topics"
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTopic = ({ item, index }: { item: TopicType; index: number }) => (
|
||||||
|
<TopicItem item={item} index={index} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectionContext = {
|
||||||
|
selection: selection,
|
||||||
|
enabled,
|
||||||
|
setEnabled,
|
||||||
|
toggleSelection: (item: TopicType) => {
|
||||||
|
setSelection((state) => {
|
||||||
|
const selection = [...state];
|
||||||
|
const index = selection.findIndex(
|
||||||
|
(selected) => selected.id === item.id
|
||||||
|
);
|
||||||
|
if (index > -1) {
|
||||||
|
selection.splice(index, 1);
|
||||||
|
if (selection.length === 0) {
|
||||||
|
setEnabled(false);
|
||||||
|
}
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
selection.push(item);
|
||||||
|
return selection;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (canShow) {
|
||||||
|
const isTopic = currentScreen.name === "TopicNotes";
|
||||||
|
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
|
||||||
|
if (!ref.current?.isOpen()) {
|
||||||
|
animations.display.setValue(5000);
|
||||||
|
animations.opacity.setValue(0);
|
||||||
|
}
|
||||||
|
if (id) {
|
||||||
|
onRequestUpdate({
|
||||||
|
item: db.notebooks?.notebook(id).data
|
||||||
|
} as any);
|
||||||
|
}
|
||||||
|
ref.current?.show();
|
||||||
|
} else {
|
||||||
|
ref.current?.hide();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
animations.display,
|
||||||
|
animations.opacity,
|
||||||
|
canShow,
|
||||||
|
currentScreen?.id,
|
||||||
|
currentScreen.name,
|
||||||
|
currentScreen?.notebookId,
|
||||||
|
onRequestUpdate
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionSheet
|
||||||
|
ref={ref}
|
||||||
|
isModal={false}
|
||||||
|
containerStyle={{
|
||||||
|
height: 530,
|
||||||
|
maxHeight: 800,
|
||||||
|
borderTopRightRadius: 15,
|
||||||
|
borderTopLeftRadius: 15,
|
||||||
|
backgroundColor: colors.bg,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.border,
|
||||||
|
borderBottomWidth: 0
|
||||||
|
}}
|
||||||
|
closable={!canShow}
|
||||||
|
elevation={10}
|
||||||
|
indicatorStyle={{
|
||||||
|
width: 100,
|
||||||
|
backgroundColor: colors.nav
|
||||||
|
}}
|
||||||
|
keyboardHandlerEnabled={false}
|
||||||
|
snapPoints={[15, 60, 100]}
|
||||||
|
initialSnapIndex={0}
|
||||||
|
backgroundInteractionEnabled
|
||||||
|
enableGesturesInScrollView
|
||||||
|
onChange={(position, height) => {
|
||||||
|
animations.translate.setValue(position);
|
||||||
|
const h = Dimensions.get("window").height;
|
||||||
|
const minPos = h - height;
|
||||||
|
if (position - 100 < minPos || !canShow) {
|
||||||
|
animations.display.setValue(5000);
|
||||||
|
animations.opacity.setValue(0);
|
||||||
|
} else {
|
||||||
|
animations.display.setValue(0);
|
||||||
|
setTimeout(() => {
|
||||||
|
animations.opacity.setValue(1);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
gestureEnabled
|
||||||
|
ExtraOverlayComponent={
|
||||||
|
<Animated.View
|
||||||
|
style={{
|
||||||
|
top: animations.translate,
|
||||||
|
position: "absolute",
|
||||||
|
right: 12,
|
||||||
|
opacity: animations.opacity,
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateY: animations.display
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PressableButton
|
||||||
|
type="accent"
|
||||||
|
accentColor={"accent"}
|
||||||
|
accentText="light"
|
||||||
|
onPress={openEditor}
|
||||||
|
customStyle={{
|
||||||
|
borderRadius: 100,
|
||||||
|
bottom: 50
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
height: normalize(60),
|
||||||
|
width: normalize(60)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="plus" color="white" size={SIZE.xxl} />
|
||||||
|
</View>
|
||||||
|
</PressableButton>
|
||||||
|
</Animated.View>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
height: 530,
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
alignItems: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||||
|
TOPICS
|
||||||
|
</Paragraph>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{enabled ? (
|
||||||
|
<IconButton
|
||||||
|
customStyle={{
|
||||||
|
marginLeft: 10
|
||||||
|
}}
|
||||||
|
onPress={async () => {
|
||||||
|
//@ts-ignore
|
||||||
|
history.selectedItemsList = selection;
|
||||||
|
presentDialog({
|
||||||
|
title: `Delete ${
|
||||||
|
selection.length > 1 ? "topics" : "topics"
|
||||||
|
}`,
|
||||||
|
paragraph: `Are you sure you want to delete ${
|
||||||
|
selection.length > 1 ? "these topicss?" : "this topics?"
|
||||||
|
}`,
|
||||||
|
positiveText: "Delete",
|
||||||
|
negativeText: "Cancel",
|
||||||
|
positivePress: async () => {
|
||||||
|
await deleteItems();
|
||||||
|
history.selectedItemsList = [];
|
||||||
|
setEnabled(false);
|
||||||
|
setSelection([]);
|
||||||
|
},
|
||||||
|
positiveType: "errorShade"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
color={colors.pri}
|
||||||
|
tooltipText="Move to trash"
|
||||||
|
tooltipPosition={1}
|
||||||
|
name="delete"
|
||||||
|
size={22}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconButton
|
||||||
|
name="plus"
|
||||||
|
onPress={PLACEHOLDER_DATA.action}
|
||||||
|
color={colors.pri}
|
||||||
|
size={22}
|
||||||
|
customStyle={{
|
||||||
|
width: 40,
|
||||||
|
height: 40
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<SelectionContext.Provider value={selectionContext}>
|
||||||
|
<FlatList
|
||||||
|
data={topics}
|
||||||
|
style={{
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl
|
||||||
|
refreshing={false}
|
||||||
|
onRefresh={() => {
|
||||||
|
onRequestUpdate();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
keyExtractor={(item) => item.id}
|
||||||
|
renderItem={renderTopic}
|
||||||
|
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||||
|
/>
|
||||||
|
</SelectionContext.Provider>
|
||||||
|
</View>
|
||||||
|
</ActionSheet>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SelectionContext = createContext<{
|
||||||
|
selection: TopicType[];
|
||||||
|
enabled: boolean;
|
||||||
|
setEnabled: (value: boolean) => void;
|
||||||
|
toggleSelection: (item: TopicType) => void;
|
||||||
|
}>({
|
||||||
|
selection: [],
|
||||||
|
enabled: false,
|
||||||
|
setEnabled: (value: boolean) => {},
|
||||||
|
toggleSelection: (item: TopicType) => {}
|
||||||
|
});
|
||||||
|
const useSelection = () => useContext(SelectionContext);
|
||||||
|
|
||||||
|
const TopicItem = ({ item }: { item: TopicType; index: number }) => {
|
||||||
|
const screen = useNavigationStore((state) => state.currentScreen);
|
||||||
|
const colors = useThemeStore((state) => state.colors);
|
||||||
|
const selection = useSelection();
|
||||||
|
const isSelected =
|
||||||
|
selection.selection.findIndex((selected) => selected.id === item.id) > -1;
|
||||||
|
const isFocused = screen.id === item.id;
|
||||||
|
const notesCount = getTotalNotes(item);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PressableButton
|
||||||
|
type={isSelected || isFocused ? "grayBg" : "transparent"}
|
||||||
|
onLongPress={() => {
|
||||||
|
if (selection.enabled) return;
|
||||||
|
selection.setEnabled(true);
|
||||||
|
selection.toggleSelection(item);
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
if (selection.enabled) {
|
||||||
|
selection.toggleSelection(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TopicNotes.navigate(item, true);
|
||||||
|
}}
|
||||||
|
customStyle={{
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "row",
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
borderRadius: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selection.enabled ? (
|
||||||
|
<IconButton
|
||||||
|
size={SIZE.lg}
|
||||||
|
color={isSelected ? colors.accent : colors.icon}
|
||||||
|
name={
|
||||||
|
isSelected
|
||||||
|
? "check-circle-outline"
|
||||||
|
: "checkbox-blank-circle-outline"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Paragraph size={SIZE.sm}>
|
||||||
|
{item.title}{" "}
|
||||||
|
{notesCount ? (
|
||||||
|
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||||
|
{notesCount}
|
||||||
|
</Paragraph>
|
||||||
|
) : null}
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
<IconButton
|
||||||
|
name="dots-horizontal"
|
||||||
|
customStyle={{
|
||||||
|
width: 40,
|
||||||
|
height: 40
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
Properties.present(item);
|
||||||
|
}}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
top={0}
|
||||||
|
color={colors.pri}
|
||||||
|
size={SIZE.xl}
|
||||||
|
/>
|
||||||
|
</PressableButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -19,10 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Linking, View } from "react-native";
|
import { Linking, View } from "react-native";
|
||||||
|
import { ScrollView } from "react-native-actions-sheet";
|
||||||
import { checkVersion } from "react-native-check-version";
|
import { checkVersion } from "react-native-check-version";
|
||||||
import Config from "react-native-config";
|
import Config from "react-native-config";
|
||||||
import deviceInfoModule from "react-native-device-info";
|
import deviceInfoModule from "react-native-device-info";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
|
||||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||||
import { STORE_LINK } from "../../../utils/constants";
|
import { STORE_LINK } from "../../../utils/constants";
|
||||||
import { SIZE } from "../../../utils/size";
|
import { SIZE } from "../../../utils/size";
|
||||||
@@ -148,9 +148,6 @@ export const Update = ({ version: appVersion, fwdRef }) => {
|
|||||||
<Seperator />
|
<Seperator />
|
||||||
<ScrollView
|
<ScrollView
|
||||||
nestedScrollEnabled={true}
|
nestedScrollEnabled={true}
|
||||||
onMomentumScrollEnd={() => {
|
|
||||||
fwdRef?.current?.handleChildScrollEnd();
|
|
||||||
}}
|
|
||||||
style={{
|
style={{
|
||||||
width: "100%"
|
width: "100%"
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ const SheetWrapper = ({
|
|||||||
onHasReachedTop,
|
onHasReachedTop,
|
||||||
keyboardMode,
|
keyboardMode,
|
||||||
overlay,
|
overlay,
|
||||||
overlayOpacity = 0.3
|
overlayOpacity = 0.3,
|
||||||
|
enableGesturesInScrollView = false
|
||||||
}) => {
|
}) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||||
@@ -59,8 +60,8 @@ const SheetWrapper = ({
|
|||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
paddingTop: 5,
|
paddingTop: 5,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
borderTopRightRadius: 20,
|
borderTopRightRadius: 15,
|
||||||
borderTopLeftRadius: 20,
|
borderTopLeftRadius: 15,
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
borderBottomRightRadius: 0,
|
borderBottomRightRadius: 0,
|
||||||
borderBottomLeftRadius: 0
|
borderBottomLeftRadius: 0
|
||||||
@@ -84,7 +85,8 @@ const SheetWrapper = ({
|
|||||||
backdrop: "sheet-backdrop"
|
backdrop: "sheet-backdrop"
|
||||||
}}
|
}}
|
||||||
indicatorStyle={{
|
indicatorStyle={{
|
||||||
width: 100
|
width: 100,
|
||||||
|
backgroundColor: colors.nav
|
||||||
}}
|
}}
|
||||||
drawUnderStatusBar={false}
|
drawUnderStatusBar={false}
|
||||||
containerStyle={style}
|
containerStyle={style}
|
||||||
@@ -98,8 +100,9 @@ const SheetWrapper = ({
|
|||||||
indicatorColor={colors.nav}
|
indicatorColor={colors.nav}
|
||||||
onOpen={_onOpen}
|
onOpen={_onOpen}
|
||||||
keyboardDismissMode="none"
|
keyboardDismissMode="none"
|
||||||
|
enableGesturesInScrollView={enableGesturesInScrollView}
|
||||||
defaultOverlayOpacity={overlayOpacity}
|
defaultOverlayOpacity={overlayOpacity}
|
||||||
overlayColor={pitchBlack ? "#585858" : "#000000"}
|
overlayColor={pitchBlack ? "#585858" : "#2b2b2b"}
|
||||||
keyboardShouldPersistTaps="always"
|
keyboardShouldPersistTaps="always"
|
||||||
ExtraOverlayComponent={
|
ExtraOverlayComponent={
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -22,10 +22,15 @@ import React, { useCallback, useEffect, useState } from "react";
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
import Share from "react-native-share";
|
import Share from "react-native-share";
|
||||||
import { db } from "../common/database";
|
import { db } from "../common/database";
|
||||||
|
import { AttachmentDialog } from "../components/attachments";
|
||||||
import { presentDialog } from "../components/dialog/functions";
|
import { presentDialog } from "../components/dialog/functions";
|
||||||
import NoteHistory from "../components/note-history";
|
import NoteHistory from "../components/note-history";
|
||||||
|
import { AddNotebookSheet } from "../components/sheets/add-notebook";
|
||||||
|
import MoveNoteSheet from "../components/sheets/add-to";
|
||||||
import ExportNotesSheet from "../components/sheets/export-notes";
|
import ExportNotesSheet from "../components/sheets/export-notes";
|
||||||
import { MoveNotes } from "../components/sheets/move-notes/movenote";
|
import { MoveNotes } from "../components/sheets/move-notes/movenote";
|
||||||
|
import PublishNoteSheet from "../components/sheets/publish-note";
|
||||||
|
import { RelationsList } from "../components/sheets/relations-list/index";
|
||||||
import ReminderSheet from "../components/sheets/reminder";
|
import ReminderSheet from "../components/sheets/reminder";
|
||||||
import {
|
import {
|
||||||
eSendEvent,
|
eSendEvent,
|
||||||
@@ -40,24 +45,16 @@ import Notifications from "../services/notifications";
|
|||||||
import { useEditorStore } from "../stores/use-editor-store";
|
import { useEditorStore } from "../stores/use-editor-store";
|
||||||
import { useMenuStore } from "../stores/use-menu-store";
|
import { useMenuStore } from "../stores/use-menu-store";
|
||||||
import useNavigationStore from "../stores/use-navigation-store";
|
import useNavigationStore from "../stores/use-navigation-store";
|
||||||
|
import { useRelationStore } from "../stores/use-relation-store";
|
||||||
import { useSelectionStore } from "../stores/use-selection-store";
|
import { useSelectionStore } from "../stores/use-selection-store";
|
||||||
import { useTagStore } from "../stores/use-tag-store";
|
import { useTagStore } from "../stores/use-tag-store";
|
||||||
import { useThemeStore } from "../stores/use-theme-store";
|
import { useThemeStore } from "../stores/use-theme-store";
|
||||||
import { useUserStore } from "../stores/use-user-store";
|
import { useUserStore } from "../stores/use-user-store";
|
||||||
import { toTXT } from "../utils";
|
import { toTXT } from "../utils";
|
||||||
import { toggleDarkMode } from "../utils/color-scheme/utils";
|
import { toggleDarkMode } from "../utils/color-scheme/utils";
|
||||||
import {
|
import { eOpenAddTopicDialog, eOpenLoginDialog } from "../utils/events";
|
||||||
eOpenAddNotebookDialog,
|
|
||||||
eOpenAddTopicDialog,
|
|
||||||
eOpenAttachmentsDialog,
|
|
||||||
eOpenLoginDialog,
|
|
||||||
eOpenMoveNoteDialog,
|
|
||||||
eOpenPublishNoteDialog
|
|
||||||
} from "../utils/events";
|
|
||||||
import { deleteItems } from "../utils/functions";
|
import { deleteItems } from "../utils/functions";
|
||||||
import { sleep } from "../utils/time";
|
import { sleep } from "../utils/time";
|
||||||
import { RelationsList } from "../components/sheets/relations-list/index";
|
|
||||||
import { useRelationStore } from "../stores/use-relation-store";
|
|
||||||
|
|
||||||
export const useActions = ({ close = () => null, item }) => {
|
export const useActions = ({ close = () => null, item }) => {
|
||||||
const colors = useThemeStore((state) => state.colors);
|
const colors = useThemeStore((state) => state.colors);
|
||||||
@@ -129,12 +126,9 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addTo() {
|
function addTo() {
|
||||||
close();
|
|
||||||
clearSelection(true);
|
clearSelection(true);
|
||||||
setSelectedItem(item);
|
setSelectedItem(item);
|
||||||
setTimeout(() => {
|
MoveNoteSheet.present(item);
|
||||||
eSendEvent(eOpenMoveNoteDialog, item);
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addToFavorites() {
|
async function addToFavorites() {
|
||||||
@@ -270,9 +264,7 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
close();
|
PublishNoteSheet.present(item);
|
||||||
await sleep(300);
|
|
||||||
eSendEvent(eOpenPublishNoteDialog, item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkNoteSynced = () => {
|
const checkNoteSynced = () => {
|
||||||
@@ -538,22 +530,16 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openHistory() {
|
async function openHistory() {
|
||||||
close();
|
|
||||||
await sleep(300);
|
|
||||||
presentSheet({
|
presentSheet({
|
||||||
component: (ref) => <NoteHistory fwdRef={ref} note={item} />
|
component: (ref) => <NoteHistory fwdRef={ref} note={item} />
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showAttachments() {
|
async function showAttachments() {
|
||||||
close();
|
AttachmentDialog.present();
|
||||||
await sleep(300);
|
|
||||||
eSendEvent(eOpenAttachmentsDialog, item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportNote() {
|
async function exportNote() {
|
||||||
close();
|
|
||||||
await sleep(300);
|
|
||||||
ExportNotesSheet.present([item]);
|
ExportNotesSheet.present([item]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,8 +633,6 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
title: "Add notes",
|
title: "Add notes",
|
||||||
icon: "plus",
|
icon: "plus",
|
||||||
func: async () => {
|
func: async () => {
|
||||||
close();
|
|
||||||
await sleep(500);
|
|
||||||
MoveNotes.present(db.notebooks.notebook(item.notebookId).data, item);
|
MoveNotes.present(db.notebooks.notebook(item.notebookId).data, item);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -689,9 +673,7 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
title: "Edit notebook",
|
title: "Edit notebook",
|
||||||
icon: "square-edit-outline",
|
icon: "square-edit-outline",
|
||||||
func: async () => {
|
func: async () => {
|
||||||
close();
|
AddNotebookSheet.present(item);
|
||||||
await sleep(300);
|
|
||||||
eSendEvent(eOpenAddNotebookDialog, item);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -777,8 +759,6 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
title: "Edit reminder",
|
title: "Edit reminder",
|
||||||
icon: "pencil",
|
icon: "pencil",
|
||||||
func: async () => {
|
func: async () => {
|
||||||
close();
|
|
||||||
await sleep(300);
|
|
||||||
ReminderSheet.present(item);
|
ReminderSheet.present(item);
|
||||||
},
|
},
|
||||||
close: false
|
close: false
|
||||||
@@ -789,7 +769,6 @@ export const useActions = ({ close = () => null, item }) => {
|
|||||||
title: "Reminders",
|
title: "Reminders",
|
||||||
icon: "clock-outline",
|
icon: "clock-outline",
|
||||||
func: async () => {
|
func: async () => {
|
||||||
close();
|
|
||||||
RelationsList.present({
|
RelationsList.present({
|
||||||
reference: item,
|
reference: item,
|
||||||
referenceType: "reminder",
|
referenceType: "reminder",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { SafeAreaView } from "react-native";
|
|||||||
import Container from "../components/container";
|
import Container from "../components/container";
|
||||||
import DelayLayout from "../components/delay-layout";
|
import DelayLayout from "../components/delay-layout";
|
||||||
import Intro from "../components/intro";
|
import Intro from "../components/intro";
|
||||||
|
import { TopicsSheet } from "../components/sheets/topic-sheet";
|
||||||
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
import useGlobalSafeAreaInsets from "../hooks/use-global-safe-area-insets";
|
||||||
import { hideAllTooltips } from "../hooks/use-tooltip";
|
import { hideAllTooltips } from "../hooks/use-tooltip";
|
||||||
import Favorites from "../screens/favorites";
|
import Favorites from "../screens/favorites";
|
||||||
@@ -198,6 +199,7 @@ const _NavigationStack = () => {
|
|||||||
<NavigationContainer onStateChange={onStateChange} ref={rootNavigatorRef}>
|
<NavigationContainer onStateChange={onStateChange} ref={rootNavigatorRef}>
|
||||||
<Tabs />
|
<Tabs />
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
<TopicsSheet />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
"main": "./App.js",
|
"main": "./App.js",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"react": "18.0.0",
|
||||||
|
"react-native": "0.69.7",
|
||||||
"@flyerhq/react-native-link-preview": "^1.6.0",
|
"@flyerhq/react-native-link-preview": "^1.6.0",
|
||||||
"@mdi/js": "^6.7.96",
|
"@mdi/js": "^6.7.96",
|
||||||
"absolutify": "^0.1.0",
|
"absolutify": "^0.1.0",
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
"html-to-text": "8.1.0",
|
"html-to-text": "8.1.0",
|
||||||
"phone": "^3.1.14",
|
"phone": "^3.1.14",
|
||||||
"qclone": "^1.2.0",
|
"qclone": "^1.2.0",
|
||||||
"react-native-actions-sheet": "^0.7.2",
|
"react-native-actions-sheet": "^0.9.0-alpha.6",
|
||||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||||
"react-native-drax": "^0.10.2",
|
"react-native-drax": "^0.10.2",
|
||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
@@ -28,7 +30,6 @@
|
|||||||
"zustand": "^3.6.0",
|
"zustand": "^3.6.0",
|
||||||
"fflate": "^0.7.3",
|
"fflate": "^0.7.3",
|
||||||
"timeago.js": "4.0.2"
|
"timeago.js": "4.0.2"
|
||||||
|
|
||||||
},
|
},
|
||||||
"sideEffects": false
|
"sideEffects": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
translateValue.value = 6000;
|
translateValue.value = 6000;
|
||||||
}, 500);
|
}, 500);
|
||||||
}, 100);
|
}, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[opacity, translateValue]
|
[opacity, translateValue]
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { WebViewMessageEvent } from "react-native-webview";
|
import { WebViewMessageEvent } from "react-native-webview";
|
||||||
import { db } from "../../../common/database";
|
import { db } from "../../../common/database";
|
||||||
import ImagePreview from "../../../components/image-preview";
|
import ManageTagsSheet from "../../../components/sheets/manage-tags";
|
||||||
import { RelationsList } from "../../../components/sheets/relations-list";
|
import { RelationsList } from "../../../components/sheets/relations-list";
|
||||||
import ReminderSheet from "../../../components/sheets/reminder";
|
import ReminderSheet from "../../../components/sheets/reminder";
|
||||||
import useKeyboard from "../../../hooks/use-keyboard";
|
import useKeyboard from "../../../hooks/use-keyboard";
|
||||||
@@ -52,8 +52,7 @@ import {
|
|||||||
eOpenFullscreenEditor,
|
eOpenFullscreenEditor,
|
||||||
eOpenLoginDialog,
|
eOpenLoginDialog,
|
||||||
eOpenPremiumDialog,
|
eOpenPremiumDialog,
|
||||||
eOpenPublishNoteDialog,
|
eOpenPublishNoteDialog
|
||||||
eOpenTagsDialog
|
|
||||||
} from "../../../utils/events";
|
} from "../../../utils/events";
|
||||||
import { openLinkInBrowser } from "../../../utils/functions";
|
import { openLinkInBrowser } from "../../../utils/functions";
|
||||||
import { tabBarRef } from "../../../utils/global-refs";
|
import { tabBarRef } from "../../../utils/global-refs";
|
||||||
@@ -327,7 +326,7 @@ export const useEditorEvents = (
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
eSendEvent(eOpenTagsDialog, editor.note.current);
|
ManageTagsSheet.present(editor.note.current);
|
||||||
break;
|
break;
|
||||||
case EventTypes.tag:
|
case EventTypes.tag:
|
||||||
if (editorMessage.value) {
|
if (editorMessage.value) {
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ export const useEditor = (
|
|||||||
id = await db.notes?.add(noteData);
|
id = await db.notes?.add(noteData);
|
||||||
if (!note && id) {
|
if (!note && id) {
|
||||||
currentNote.current = db.notes?.note(id).data as NoteType;
|
currentNote.current = db.notes?.note(id).data as NoteType;
|
||||||
|
console.log("on Note Created", state.current?.onNoteCreated);
|
||||||
state.current?.onNoteCreated && state.current.onNoteCreated(id);
|
state.current?.onNoteCreated && state.current.onNoteCreated(id);
|
||||||
if (!noteData.title) {
|
if (!noteData.title) {
|
||||||
postMessage(
|
postMessage(
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
|||||||
onBlur: () => false,
|
onBlur: () => false,
|
||||||
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
delay: SettingsService.get().homepage === route.name ? 1 : -1
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DelayLayout wait={loading} delay={500}>
|
<DelayLayout wait={loading} delay={500}>
|
||||||
<List
|
<List
|
||||||
|
|||||||
@@ -16,12 +16,9 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||||
import qclone from "qclone";
|
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { db } from "../../common/database";
|
import { db } from "../../common/database";
|
||||||
import { FloatingButton } from "../../components/container/floating-button";
|
|
||||||
import DelayLayout from "../../components/delay-layout";
|
import DelayLayout from "../../components/delay-layout";
|
||||||
import List from "../../components/list";
|
import List from "../../components/list";
|
||||||
import { NotebookHeader } from "../../components/list-items/headers/notebook-header";
|
import { NotebookHeader } from "../../components/list-items/headers/notebook-header";
|
||||||
@@ -36,28 +33,29 @@ import SearchService from "../../services/search";
|
|||||||
import useNavigationStore, {
|
import useNavigationStore, {
|
||||||
NotebookScreenParams
|
NotebookScreenParams
|
||||||
} from "../../stores/use-navigation-store";
|
} from "../../stores/use-navigation-store";
|
||||||
import {
|
import { eOnNewTopicAdded, eOpenAddNotebookDialog } from "../../utils/events";
|
||||||
eOnNewTopicAdded,
|
|
||||||
eOpenAddNotebookDialog,
|
|
||||||
eOpenAddTopicDialog
|
|
||||||
} from "../../utils/events";
|
|
||||||
import { NotebookType } from "../../utils/types";
|
import { NotebookType } from "../../utils/types";
|
||||||
|
import { openEditor, setOnFirstSave } from "../notes/common";
|
||||||
const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||||
const [topics, setTopics] = useState(
|
const [notes, setNotes] = useState(
|
||||||
groupArray(
|
groupArray(
|
||||||
qclone(route?.params.item?.topics) || [],
|
db.relations?.from(route.params.item, "note") || [],
|
||||||
db.settings?.getGroupOptions("topics")
|
db.settings?.getGroupOptions("notes")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const params = useRef<NotebookScreenParams>(route?.params);
|
const params = useRef<NotebookScreenParams>(route?.params);
|
||||||
|
|
||||||
useNavigationFocus(navigation, {
|
useNavigationFocus(navigation, {
|
||||||
onFocus: () => {
|
onFocus: () => {
|
||||||
Navigation.routeNeedsUpdate(route.name, onRequestUpdate);
|
Navigation.routeNeedsUpdate(route.name, onRequestUpdate);
|
||||||
syncWithNavigation();
|
syncWithNavigation();
|
||||||
useNavigationStore.getState().setButtonAction(onPressFloatingButton);
|
useNavigationStore.getState().setButtonAction(openEditor);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
onBlur: () => false
|
onBlur: () => {
|
||||||
|
setOnFirstSave(null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const syncWithNavigation = React.useCallback(() => {
|
const syncWithNavigation = React.useCallback(() => {
|
||||||
@@ -70,6 +68,10 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
|||||||
},
|
},
|
||||||
params.current?.canGoBack
|
params.current?.canGoBack
|
||||||
);
|
);
|
||||||
|
setOnFirstSave({
|
||||||
|
type: "notebook",
|
||||||
|
id: params.current.item.id
|
||||||
|
});
|
||||||
SearchService.prepareSearch = prepareSearch;
|
SearchService.prepareSearch = prepareSearch;
|
||||||
}, [route.name]);
|
}, [route.name]);
|
||||||
|
|
||||||
@@ -82,11 +84,9 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
|||||||
?.data as NotebookType;
|
?.data as NotebookType;
|
||||||
if (notebook) {
|
if (notebook) {
|
||||||
params.current.item = notebook;
|
params.current.item = notebook;
|
||||||
setTopics(
|
const notes = db.relations?.from(notebook, "note");
|
||||||
groupArray(
|
setNotes(
|
||||||
qclone(notebook.topics),
|
groupArray(notes || [], db.settings?.getGroupOptions("notes"))
|
||||||
db.settings?.getGroupOptions("topics")
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
syncWithNavigation();
|
syncWithNavigation();
|
||||||
}
|
}
|
||||||
@@ -102,60 +102,61 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
|||||||
return () => {
|
return () => {
|
||||||
eUnSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
|
eUnSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
|
||||||
};
|
};
|
||||||
}, [onRequestUpdate, topics]);
|
}, [onRequestUpdate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setOnFirstSave(null);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const prepareSearch = () => {
|
const prepareSearch = () => {
|
||||||
SearchService.update({
|
SearchService.update({
|
||||||
placeholder: `Search in "${params.current.title}"`,
|
placeholder: `Search in "${params.current.title}"`,
|
||||||
type: "topics",
|
type: "notes",
|
||||||
title: params.current.title,
|
title: params.current.title,
|
||||||
get: () => {
|
get: () => {
|
||||||
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
|
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
|
||||||
?.data as NotebookType;
|
?.data as NotebookType;
|
||||||
return notebook?.topics;
|
return db.relations?.from(notebook, "note");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPressFloatingButton = () => {
|
|
||||||
const n = params.current.item;
|
|
||||||
eSendEvent(eOpenAddTopicDialog, { notebookId: n.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
const PLACEHOLDER_DATA = {
|
const PLACEHOLDER_DATA = {
|
||||||
heading: params.current.item?.title,
|
heading: params.current.item?.title,
|
||||||
paragraph: "You have not added any topics yet.",
|
paragraph: "You have not added any notes yet.",
|
||||||
button: "Add first topic",
|
button: "Add first note",
|
||||||
action: onPressFloatingButton,
|
action: openEditor,
|
||||||
loading: "Loading notebook topics"
|
loading: "Loading notebook notes"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DelayLayout>
|
<>
|
||||||
<List
|
<DelayLayout>
|
||||||
listData={topics}
|
<List
|
||||||
type="topics"
|
listData={notes}
|
||||||
refreshCallback={() => {
|
type="notes"
|
||||||
onRequestUpdate();
|
refreshCallback={() => {
|
||||||
}}
|
onRequestUpdate();
|
||||||
screen="Notebook"
|
}}
|
||||||
headerProps={{
|
screen="Notebook"
|
||||||
heading: params.current.title
|
headerProps={{
|
||||||
}}
|
heading: params.current.title
|
||||||
loading={false}
|
}}
|
||||||
ListHeader={
|
loading={false}
|
||||||
<NotebookHeader
|
ListHeader={
|
||||||
onEditNotebook={() => {
|
<NotebookHeader
|
||||||
eSendEvent(eOpenAddNotebookDialog, params.current.item);
|
onEditNotebook={() => {
|
||||||
}}
|
eSendEvent(eOpenAddNotebookDialog, params.current.item);
|
||||||
notebook={params.current.item}
|
}}
|
||||||
/>
|
notebook={params.current.item}
|
||||||
}
|
/>
|
||||||
placeholderData={PLACEHOLDER_DATA}
|
}
|
||||||
/>
|
placeholderData={PLACEHOLDER_DATA}
|
||||||
|
/>
|
||||||
<FloatingButton title="Add new topic" onPress={onPressFloatingButton} />
|
</DelayLayout>
|
||||||
</DelayLayout>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Config } from "react-native-config";
|
||||||
import { db } from "../../common/database";
|
import { db } from "../../common/database";
|
||||||
import { FloatingButton } from "../../components/container/floating-button";
|
import { FloatingButton } from "../../components/container/floating-button";
|
||||||
import DelayLayout from "../../components/delay-layout";
|
import DelayLayout from "../../components/delay-layout";
|
||||||
import { AddNotebookEvent } from "../../components/dialog-provider/recievers";
|
|
||||||
import List from "../../components/list";
|
import List from "../../components/list";
|
||||||
|
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
|
||||||
import { Walkthrough } from "../../components/walkthroughs";
|
import { Walkthrough } from "../../components/walkthroughs";
|
||||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||||
@@ -30,10 +31,9 @@ import SearchService from "../../services/search";
|
|||||||
import SettingsService from "../../services/settings";
|
import SettingsService from "../../services/settings";
|
||||||
import useNavigationStore from "../../stores/use-navigation-store";
|
import useNavigationStore from "../../stores/use-navigation-store";
|
||||||
import { useNotebookStore } from "../../stores/use-notebook-store";
|
import { useNotebookStore } from "../../stores/use-notebook-store";
|
||||||
import { Config } from "react-native-config";
|
|
||||||
|
|
||||||
const onPressFloatingButton = () => {
|
const onPressFloatingButton = () => {
|
||||||
AddNotebookEvent();
|
AddNotebookSheet.present();
|
||||||
};
|
};
|
||||||
|
|
||||||
const prepareSearch = () => {
|
const prepareSearch = () => {
|
||||||
@@ -83,7 +83,7 @@ export const Notebooks = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DelayLayout>
|
<DelayLayout delay={1}>
|
||||||
<List
|
<List
|
||||||
listData={notebooks}
|
listData={notebooks}
|
||||||
type="notebooks"
|
type="notebooks"
|
||||||
|
|||||||
@@ -81,12 +81,31 @@ export const setOnFirstSave = (
|
|||||||
editorState().onNoteCreated = null;
|
editorState().onNoteCreated = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editorState().onNoteCreated = (id) => onNoteCreated(id, data);
|
setTimeout(() => {
|
||||||
|
editorState().onNoteCreated = (id) => onNoteCreated(id, data);
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function onNoteCreated(id: string, params: FirstSaveData) {
|
async function onNoteCreated(id: string, params: FirstSaveData) {
|
||||||
if (!params) return;
|
if (!params) return;
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
|
case "notebook": {
|
||||||
|
await db.relations?.add(
|
||||||
|
{ type: "notebook", id: params.id },
|
||||||
|
{ type: "note", id: id }
|
||||||
|
);
|
||||||
|
Navigation.queueRoutesForUpdate(
|
||||||
|
"TaggedNotes",
|
||||||
|
"ColoredNotes",
|
||||||
|
"TopicNotes",
|
||||||
|
"Favorites",
|
||||||
|
"Notes",
|
||||||
|
"Notebook",
|
||||||
|
"Notebooks"
|
||||||
|
);
|
||||||
|
editorState().onNoteCreated = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "topic": {
|
case "topic": {
|
||||||
if (!params.notebook) break;
|
if (!params.notebook) break;
|
||||||
await db.notes?.addToNotebook(
|
await db.notes?.addToNotebook(
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ import {
|
|||||||
setOnFirstSave,
|
setOnFirstSave,
|
||||||
toCamelCase
|
toCamelCase
|
||||||
} from "./common";
|
} from "./common";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { db } from "../../common/database";
|
||||||
|
import Paragraph from "../../components/ui/typography/paragraph";
|
||||||
|
import { IconButton } from "../../components/ui/icon-button";
|
||||||
|
import { useThemeStore } from "../../stores/use-theme-store";
|
||||||
|
import { SIZE } from "../../utils/size";
|
||||||
|
import Notebook from "../notebook/index";
|
||||||
export const WARNING_DATA = {
|
export const WARNING_DATA = {
|
||||||
title: "Some notes in this topic are not synced"
|
title: "Some notes in this topic are not synced"
|
||||||
};
|
};
|
||||||
@@ -92,13 +99,18 @@ const NotesPage = ({
|
|||||||
}: RouteProps<
|
}: RouteProps<
|
||||||
"NotesPage" | "TaggedNotes" | "Monographs" | "ColoredNotes" | "TopicNotes"
|
"NotesPage" | "TaggedNotes" | "Monographs" | "ColoredNotes" | "TopicNotes"
|
||||||
>) => {
|
>) => {
|
||||||
|
const colors = useThemeStore((state) => state.colors);
|
||||||
const params = useRef<NotesScreenParams>(route?.params);
|
const params = useRef<NotesScreenParams>(route?.params);
|
||||||
const [notes, setNotes] = useState<NoteType[]>(get(route.params, true));
|
const [notes, setNotes] = useState<NoteType[]>(get(route.params, true));
|
||||||
const loading = useNoteStore((state) => state.loading);
|
const loading = useNoteStore((state) => state.loading);
|
||||||
const [loadingNotes, setLoadingNotes] = useState(false);
|
const [loadingNotes, setLoadingNotes] = useState(false);
|
||||||
const alias = getAlias(params.current);
|
const alias = getAlias(params.current);
|
||||||
const isMonograph = route.name === "Monographs";
|
const isMonograph = route.name === "Monographs";
|
||||||
|
const notebook =
|
||||||
|
route.name === "TopicNotes" && (params.current.item as TopicType).notebookId
|
||||||
|
? db.notebooks?.notebook((params.current.item as TopicType).notebookId)
|
||||||
|
?.data
|
||||||
|
: null;
|
||||||
const isFocused = useNavigationFocus(navigation, {
|
const isFocused = useNavigationFocus(navigation, {
|
||||||
onFocus: (prev) => {
|
onFocus: (prev) => {
|
||||||
Navigation.routeNeedsUpdate(route.name, onRequestUpdate);
|
Navigation.routeNeedsUpdate(route.name, onRequestUpdate);
|
||||||
@@ -176,6 +188,7 @@ const NotesPage = ({
|
|||||||
) {
|
) {
|
||||||
return Navigation.goBack();
|
return Navigation.goBack();
|
||||||
}
|
}
|
||||||
|
if (notes.length === 0) setLoadingNotes(false);
|
||||||
setNotes(notes);
|
setNotes(notes);
|
||||||
syncWithNavigation();
|
syncWithNavigation();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -187,7 +200,7 @@ const NotesPage = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loadingNotes) {
|
if (loadingNotes) {
|
||||||
setTimeout(() => setLoadingNotes(false), 300);
|
setTimeout(() => setLoadingNotes(false), 50);
|
||||||
}
|
}
|
||||||
}, [loadingNotes, notes]);
|
}, [loadingNotes, notes]);
|
||||||
|
|
||||||
@@ -208,6 +221,45 @@ const NotesPage = ({
|
|||||||
}
|
}
|
||||||
wait={loading || loadingNotes}
|
wait={loading || loadingNotes}
|
||||||
>
|
>
|
||||||
|
{route.name === "TopicNotes" ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center"
|
||||||
|
// borderBottomWidth: 1,
|
||||||
|
// borderBottomColor: colors.nav
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paragraph
|
||||||
|
onPress={() => {
|
||||||
|
Navigation.navigate(
|
||||||
|
{
|
||||||
|
name: "Notebooks"
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
size={SIZE.xs}
|
||||||
|
>
|
||||||
|
Notebooks
|
||||||
|
</Paragraph>
|
||||||
|
<IconButton
|
||||||
|
name="chevron-right"
|
||||||
|
size={14}
|
||||||
|
customStyle={{ width: 25, height: 25 }}
|
||||||
|
/>
|
||||||
|
<Paragraph
|
||||||
|
onPress={() => {
|
||||||
|
Notebook.navigate(notebook, true);
|
||||||
|
}}
|
||||||
|
size={SIZE.xs}
|
||||||
|
>
|
||||||
|
{notebook.title}
|
||||||
|
</Paragraph>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
<List
|
<List
|
||||||
listData={notes}
|
listData={notes}
|
||||||
type="notes"
|
type="notes"
|
||||||
|
|||||||
@@ -58,16 +58,18 @@ export const TopicNotes = ({
|
|||||||
route
|
route
|
||||||
}: NavigationProps<"TopicNotes">) => {
|
}: NavigationProps<"TopicNotes">) => {
|
||||||
return (
|
return (
|
||||||
<NotesPage
|
<>
|
||||||
navigation={navigation}
|
<NotesPage
|
||||||
route={route}
|
navigation={navigation}
|
||||||
get={TopicNotes.get}
|
route={route}
|
||||||
placeholderData={PLACEHOLDER_DATA}
|
get={TopicNotes.get}
|
||||||
onPressFloatingButton={openEditor}
|
placeholderData={PLACEHOLDER_DATA}
|
||||||
rightButtons={headerRightButtons}
|
onPressFloatingButton={openEditor}
|
||||||
canGoBack={route.params.canGoBack}
|
rightButtons={headerRightButtons}
|
||||||
focusControl={true}
|
canGoBack={route.params.canGoBack}
|
||||||
/>
|
focusControl={true}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Clipboard from "@react-native-clipboard/clipboard";
|
|
||||||
import EventManager from "@notesnook/core/utils/event-manager";
|
import EventManager from "@notesnook/core/utils/event-manager";
|
||||||
|
import Clipboard from "@react-native-clipboard/clipboard";
|
||||||
import { RefObject } from "react";
|
import { RefObject } from "react";
|
||||||
import ActionSheet from "react-native-actions-sheet";
|
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||||
import Config from "react-native-config";
|
import Config from "react-native-config";
|
||||||
import {
|
import {
|
||||||
eCloseSheet,
|
eCloseSheet,
|
||||||
@@ -97,7 +97,7 @@ export type PresentSheetOptions = {
|
|||||||
component:
|
component:
|
||||||
| JSX.Element
|
| JSX.Element
|
||||||
| ((
|
| ((
|
||||||
ref: RefObject<ActionSheet>,
|
ref: RefObject<ActionSheetRef>,
|
||||||
close?: (ctx?: string) => void,
|
close?: (ctx?: string) => void,
|
||||||
update?: (props: PresentSheetOptions) => void
|
update?: (props: PresentSheetOptions) => void
|
||||||
) => JSX.Element);
|
) => JSX.Element);
|
||||||
@@ -114,6 +114,7 @@ export type PresentSheetOptions = {
|
|||||||
actionsArray: SheetAction[];
|
actionsArray: SheetAction[];
|
||||||
learnMore: string;
|
learnMore: string;
|
||||||
learnMorePress: () => void;
|
learnMorePress: () => void;
|
||||||
|
enableGesturesInScrollView: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function presentSheet(data: Partial<PresentSheetOptions>) {
|
export function presentSheet(data: Partial<PresentSheetOptions>) {
|
||||||
|
|||||||
56
apps/mobile/package-lock.json
generated
56
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@notesnook/mobile",
|
"name": "@notesnook/mobile",
|
||||||
"version": "2.4.5",
|
"version": "2.4.6",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@notesnook/mobile",
|
"name": "@notesnook/mobile",
|
||||||
"version": "2.4.5",
|
"version": "2.4.6",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"native/",
|
"native/",
|
||||||
@@ -40,7 +40,9 @@
|
|||||||
"html-to-text": "8.1.0",
|
"html-to-text": "8.1.0",
|
||||||
"phone": "^3.1.14",
|
"phone": "^3.1.14",
|
||||||
"qclone": "^1.2.0",
|
"qclone": "^1.2.0",
|
||||||
"react-native-actions-sheet": "^0.7.2",
|
"react": "18.0.0",
|
||||||
|
"react-native": "0.69.7",
|
||||||
|
"react-native-actions-sheet": "^0.9.0-alpha.6",
|
||||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||||
"react-native-drax": "^0.10.2",
|
"react-native-drax": "^0.10.2",
|
||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
@@ -17955,10 +17957,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native-actions-sheet": {
|
"node_modules/react-native-actions-sheet": {
|
||||||
"version": "0.7.2",
|
"version": "0.9.0-alpha.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.6.tgz",
|
||||||
"integrity": "sha512-au9QkDnSC+lhiTMHYA2cNdOhrKW/6v/vdeOTNigRFuvYoVVS2+vJOJpt2Z3mumRjmD02UocV0WmHu4anSsqqpA==",
|
"integrity": "sha512-CtSttiXk+pTmuzrtfDlX5cwYEBsS2DuvZSE17iEYzbTDaYQ2ahRn7+34h4kuDmqLvp9Sdksb7C8s5pFJj+S9sA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@shopify/flash-list": "^1.4.1"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-gesture-handler": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-actions-sheet/node_modules/@shopify/flash-list": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-yM5dlhqolO/R8FKomqCrSYz0Cc82vJDikxhbu1CXXGp3rPvo/ceP9jJyKueW96SXHsn/87fcSq2BjztWjlp74Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"recyclerlistview": "4.2.0",
|
||||||
|
"tslib": "2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/runtime": "*",
|
||||||
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -24307,7 +24327,9 @@
|
|||||||
"html-to-text": "8.1.0",
|
"html-to-text": "8.1.0",
|
||||||
"phone": "^3.1.14",
|
"phone": "^3.1.14",
|
||||||
"qclone": "^1.2.0",
|
"qclone": "^1.2.0",
|
||||||
"react-native-actions-sheet": "^0.7.2",
|
"react": "18.0.0",
|
||||||
|
"react-native": "0.69.7",
|
||||||
|
"react-native-actions-sheet": "^0.9.0-alpha.6",
|
||||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||||
"react-native-drax": "^0.10.2",
|
"react-native-drax": "^0.10.2",
|
||||||
"react-native-image-zoom-viewer": "^3.0.1",
|
"react-native-image-zoom-viewer": "^3.0.1",
|
||||||
@@ -34851,9 +34873,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-native-actions-sheet": {
|
"react-native-actions-sheet": {
|
||||||
"version": "0.7.2",
|
"version": "0.9.0-alpha.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.6.tgz",
|
||||||
"integrity": "sha512-au9QkDnSC+lhiTMHYA2cNdOhrKW/6v/vdeOTNigRFuvYoVVS2+vJOJpt2Z3mumRjmD02UocV0WmHu4anSsqqpA=="
|
"integrity": "sha512-CtSttiXk+pTmuzrtfDlX5cwYEBsS2DuvZSE17iEYzbTDaYQ2ahRn7+34h4kuDmqLvp9Sdksb7C8s5pFJj+S9sA==",
|
||||||
|
"requires": {
|
||||||
|
"@shopify/flash-list": "^1.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@shopify/flash-list": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-yM5dlhqolO/R8FKomqCrSYz0Cc82vJDikxhbu1CXXGp3rPvo/ceP9jJyKueW96SXHsn/87fcSq2BjztWjlp74Q==",
|
||||||
|
"requires": {
|
||||||
|
"recyclerlistview": "4.2.0",
|
||||||
|
"tslib": "2.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"react-native-actions-shortcuts": {
|
"react-native-actions-shortcuts": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ async function inlineImage(element: HTMLImageElement, options?: FetchOptions) {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<HTMLImageElement | null>(function (resolve, reject) {
|
return new Promise<HTMLImageElement | null>(function(resolve, reject) {
|
||||||
if (element.parentElement?.tagName === "PICTURE") {
|
if (element.parentElement?.tagName === "PICTURE") {
|
||||||
element.parentElement?.replaceWith(element);
|
element.parentElement?.replaceWith(element);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,14 @@ export function ImageSettings(props: ToolProps) {
|
|||||||
popupId="imageSettings"
|
popupId="imageSettings"
|
||||||
tools={
|
tools={
|
||||||
findSelectedNode(editor, "image")?.attrs?.float
|
findSelectedNode(editor, "image")?.attrs?.float
|
||||||
? ["imageAlignLeft", "imageAlignRight", "imageProperties"]
|
? [
|
||||||
|
"downloadAttachment",
|
||||||
|
"imageAlignLeft",
|
||||||
|
"imageAlignRight",
|
||||||
|
"imageProperties"
|
||||||
|
]
|
||||||
: [
|
: [
|
||||||
|
"downloadAttachment",
|
||||||
"imageAlignLeft",
|
"imageAlignLeft",
|
||||||
"imageAlignCenter",
|
"imageAlignCenter",
|
||||||
"imageAlignRight",
|
"imageAlignRight",
|
||||||
|
|||||||
Reference in New Issue
Block a user