mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-14 18:57:50 +01:00
mobile: finalize note linking
This commit is contained in:
committed by
Abdullah Atta
parent
ef6c4bb6fb
commit
09f37ae872
@@ -74,6 +74,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
maxHeight: "100%"
|
||||
}}
|
||||
nestedScrollEnabled
|
||||
bounces={false}
|
||||
data={[0]}
|
||||
keyExtractor={() => "properties-scroll-item"}
|
||||
renderItem={() => (
|
||||
@@ -215,6 +216,8 @@ Properties.present = async (item, buttons = [], isSheet) => {
|
||||
"reminders",
|
||||
"local-only",
|
||||
"duplicate",
|
||||
"copy-link",
|
||||
"references",
|
||||
...android,
|
||||
...buttons
|
||||
]);
|
||||
|
||||
@@ -45,7 +45,6 @@ export const Items = ({ item, buttons, close }) => {
|
||||
|
||||
const _renderRowItem = ({ item }) => (
|
||||
<View
|
||||
onPress={item.func}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
@@ -117,7 +116,6 @@ export const Items = ({ item, buttons, close }) => {
|
||||
);
|
||||
|
||||
const renderTopBarItem = (item, index) => {
|
||||
const isLast = index === topBarItems.length;
|
||||
return (
|
||||
<Pressable
|
||||
onPress={item.func}
|
||||
@@ -126,7 +124,6 @@ export const Items = ({ item, buttons, close }) => {
|
||||
activeOpacity={1}
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
marginRight: isLast ? 0 : 10,
|
||||
paddingHorizontal: 0,
|
||||
width: topBarItemWidth
|
||||
}}
|
||||
@@ -194,6 +191,8 @@ export const Items = ({ item, buttons, close }) => {
|
||||
"history",
|
||||
"reminders",
|
||||
"attachments",
|
||||
"references",
|
||||
"copy-link",
|
||||
"trash"
|
||||
];
|
||||
|
||||
@@ -227,6 +226,9 @@ export const Items = ({ item, buttons, close }) => {
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 6
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
{topBarItems.map(renderTopBarItem)}
|
||||
</ScrollView>
|
||||
|
||||
@@ -16,7 +16,12 @@ 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 { ContentBlock, Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import {
|
||||
ContentBlock,
|
||||
Note,
|
||||
VirtualizedGrouping,
|
||||
createInternalLink
|
||||
} from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
@@ -25,10 +30,12 @@ import { db } from "../../../common/database";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { Button } from "../../ui/button";
|
||||
import type { LinkAttributes } from "@notesnook/editor/dist/extensions/link";
|
||||
import { editorController } from "../../../screens/editor/tiptap/utils";
|
||||
|
||||
const ListNoteItem = ({
|
||||
id,
|
||||
@@ -91,10 +98,25 @@ const ListBlockItem = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
columnGap: 10,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: colors.secondary.background,
|
||||
width: 25,
|
||||
height: 25,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{item.type.toUpperCase()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
<Paragraph
|
||||
style={{
|
||||
flexShrink: 1
|
||||
@@ -104,27 +126,17 @@ const ListBlockItem = ({
|
||||
? item?.content.slice(0, 200) + "..."
|
||||
: item.content}
|
||||
</Paragraph>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: colors.secondary.background,
|
||||
width: 30,
|
||||
height: 30,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{item.type.toUpperCase()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default function LinkNote() {
|
||||
export default function LinkNote(props: {
|
||||
attributes: LinkAttributes;
|
||||
resolverId: string;
|
||||
onLinkCreated: () => void;
|
||||
close?: (ctx?: string) => void;
|
||||
}) {
|
||||
const { colors } = useThemeColors();
|
||||
const query = useRef<string>();
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
@@ -141,6 +153,7 @@ export default function LinkNote() {
|
||||
setNotes(notes);
|
||||
});
|
||||
}, []);
|
||||
console.log(new URL("https://google.com").protocol);
|
||||
|
||||
const onChange = async (value: string) => {
|
||||
query.current = value;
|
||||
@@ -161,6 +174,26 @@ export default function LinkNote() {
|
||||
}
|
||||
};
|
||||
|
||||
const onCreateLink = (blockId?: string) => {
|
||||
if (!selectedNote) return;
|
||||
const link = createInternalLink(
|
||||
"note",
|
||||
selectedNote.id,
|
||||
blockId
|
||||
? {
|
||||
blockId: blockId
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
editorController.current.commands.createInternalLink(
|
||||
{
|
||||
href: link,
|
||||
title: selectedNote.title
|
||||
},
|
||||
props.resolverId
|
||||
);
|
||||
};
|
||||
|
||||
const onSelectNote = async (note: Note) => {
|
||||
setSelectedNote(note);
|
||||
inputRef.current?.clear();
|
||||
@@ -172,17 +205,16 @@ export default function LinkNote() {
|
||||
};
|
||||
|
||||
const onSelectBlock = (block: ContentBlock) => {
|
||||
setSelectedNodeId(block.id);
|
||||
onCreateLink(block.id);
|
||||
props.onLinkCreated();
|
||||
props.close?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
height: "100%",
|
||||
flexShrink: 1,
|
||||
borderWidth: 2,
|
||||
borderColor: "red"
|
||||
minHeight: 400
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -270,6 +302,9 @@ export default function LinkNote() {
|
||||
renderItem={({ item, index }) => (
|
||||
<ListBlockItem item={item} onSelectBlock={onSelectBlock} />
|
||||
)}
|
||||
style={{
|
||||
marginTop: 10
|
||||
}}
|
||||
keyExtractor={(item) => item.id}
|
||||
data={nodes}
|
||||
/>
|
||||
@@ -282,6 +317,9 @@ export default function LinkNote() {
|
||||
onSelectNote={onSelectNote}
|
||||
/>
|
||||
)}
|
||||
style={{
|
||||
marginTop: 10
|
||||
}}
|
||||
data={notes?.placeholders}
|
||||
/>
|
||||
)}
|
||||
@@ -294,14 +332,36 @@ export default function LinkNote() {
|
||||
title="Create link"
|
||||
type="accent"
|
||||
width="100%"
|
||||
onPress={() => {
|
||||
onCreateLink();
|
||||
props.onLinkCreated();
|
||||
props.close?.();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
LinkNote.present = () => {
|
||||
LinkNote.present = (attributes: LinkAttributes, resolverId: string) => {
|
||||
let didCreateLink = false;
|
||||
presentSheet({
|
||||
component: () => <LinkNote />
|
||||
component: (ref, close) => (
|
||||
<LinkNote
|
||||
attributes={attributes}
|
||||
resolverId={resolverId}
|
||||
onLinkCreated={() => {
|
||||
didCreateLink = true;
|
||||
}}
|
||||
close={close}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
if (!didCreateLink) {
|
||||
editorController?.current.commands.dismissCreateInternalLinkRequest(
|
||||
resolverId
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
521
apps/mobile/app/components/sheets/references/index.tsx
Normal file
521
apps/mobile/app/components/sheets/references/index.tsx
Normal file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
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 {
|
||||
InternalLink,
|
||||
TextSlice,
|
||||
VirtualizedGrouping,
|
||||
createInternalLink,
|
||||
highlightInternalLinks
|
||||
} from "@notesnook/core";
|
||||
import { ContentBlock, ItemReference, Note } from "@notesnook/core/dist/types";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
import { ActivityIndicator, View } from "react-native";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import create from "zustand";
|
||||
import { db } from "../../../common/database";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { eSendEvent, presentSheet } from "../../../services/event-manager";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { eOnLoadNote } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import SheetProvider from "../../sheet-provider";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
export const useExpandedStore = create<{
|
||||
expanded: {
|
||||
[id: string]: boolean;
|
||||
};
|
||||
setExpanded: (id: string) => void;
|
||||
}>((set, get) => ({
|
||||
expanded: {},
|
||||
setExpanded(id: string) {
|
||||
set({
|
||||
expanded: {
|
||||
...get().expanded,
|
||||
[id]: !get().expanded[id]
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
const ListBlockItem = ({
|
||||
item,
|
||||
onSelectBlock
|
||||
}: {
|
||||
item: ContentBlock;
|
||||
onSelectBlock: () => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
return (
|
||||
<PressableButton
|
||||
onPress={() => {
|
||||
onSelectBlock();
|
||||
}}
|
||||
type={"transparent"}
|
||||
customStyle={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
paddingLeft: 35,
|
||||
justifyContent: "flex-start",
|
||||
minHeight: 45,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
columnGap: 10
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 5,
|
||||
backgroundColor: colors.secondary.background,
|
||||
width: 25,
|
||||
height: 25,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
|
||||
{item.type.toUpperCase()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexShrink: 1,
|
||||
marginTop: 2
|
||||
}}
|
||||
>
|
||||
{item?.content.length > 200
|
||||
? item?.content.slice(0, 200) + "..."
|
||||
: item.content}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
const ListNoteInternalLink = ({
|
||||
link,
|
||||
onSelect
|
||||
}: {
|
||||
link: {
|
||||
blockId: string;
|
||||
highlightedText: [TextSlice, TextSlice, TextSlice][];
|
||||
};
|
||||
onSelect: () => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
return (
|
||||
<PressableButton
|
||||
onPress={() => {
|
||||
onSelect();
|
||||
}}
|
||||
type={"transparent"}
|
||||
customStyle={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
paddingLeft: 35,
|
||||
justifyContent: "flex-start",
|
||||
minHeight: 45
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
rowGap: 10
|
||||
}}
|
||||
>
|
||||
{link.highlightedText.map((text) => (
|
||||
<Paragraph
|
||||
key={`root_${text[0].text}`}
|
||||
style={{
|
||||
flexShrink: 1,
|
||||
marginTop: 2,
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{text.map((slice) =>
|
||||
!slice.highlighted ? (
|
||||
slice.text
|
||||
) : (
|
||||
<Paragraph
|
||||
key={slice.text}
|
||||
color={colors.selected.accent}
|
||||
style={{
|
||||
textDecorationLine: "underline",
|
||||
textDecorationColor: colors.selected.accent
|
||||
}}
|
||||
>
|
||||
{slice.text}
|
||||
</Paragraph>
|
||||
)
|
||||
)}
|
||||
</Paragraph>
|
||||
))}
|
||||
</View>
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
const ListNoteItem = ({
|
||||
id,
|
||||
items,
|
||||
onSelect,
|
||||
reference,
|
||||
internalLinks,
|
||||
listType
|
||||
}: {
|
||||
id: number;
|
||||
items: VirtualizedGrouping<Note> | undefined;
|
||||
onSelect: (item: Note, blockId?: string) => void;
|
||||
reference: Note;
|
||||
internalLinks: MutableRefObject<InternalLink<"note">[] | undefined>;
|
||||
listType: "linkedNotes" | "referencedIn";
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(id, "note", items);
|
||||
const expanded = useExpandedStore((state) =>
|
||||
!item ? false : state.expanded[item.id]
|
||||
);
|
||||
const [linkedBlocks, setLinkedBlocks] = useState<ContentBlock[]>([]);
|
||||
const [noteInternalLinks, setNoteInternalLinks] = useState<
|
||||
{
|
||||
blockId: string;
|
||||
highlightedText: [TextSlice, TextSlice, TextSlice][];
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (item?.id && expanded) {
|
||||
(async () => {
|
||||
if (listType === "linkedNotes") {
|
||||
if (linkedBlocks.length) return;
|
||||
setLoading(true);
|
||||
if (!internalLinks.current) {
|
||||
internalLinks.current = await db.notes.internalLinks(reference.id);
|
||||
}
|
||||
const noteLinks = internalLinks.current.filter(
|
||||
(link) => link.id === item.id && link.params?.blockId
|
||||
);
|
||||
|
||||
if (noteLinks.length) {
|
||||
const blocks = await db.notes.contentBlocks(item.id);
|
||||
|
||||
setLinkedBlocks(
|
||||
blocks.filter((block) =>
|
||||
noteLinks.find((link) => block.id === link.params?.blockId)
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (noteInternalLinks.length) return;
|
||||
setLoading(true);
|
||||
const blocks = await db.notes.contentBlocks(item.id);
|
||||
|
||||
setNoteInternalLinks(
|
||||
blocks
|
||||
.filter((block) =>
|
||||
block.content.includes(createInternalLink("note", reference.id))
|
||||
)
|
||||
.map((block) => {
|
||||
return {
|
||||
blockId: block?.id as string,
|
||||
highlightedText: highlightInternalLinks(
|
||||
block as ContentBlock,
|
||||
reference.id
|
||||
)
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})();
|
||||
}
|
||||
}, [
|
||||
item?.id,
|
||||
expanded,
|
||||
linkedBlocks.length,
|
||||
internalLinks,
|
||||
reference.id,
|
||||
listType,
|
||||
noteInternalLinks.length
|
||||
]);
|
||||
|
||||
const renderBlock = React.useCallback(
|
||||
(block: ContentBlock) => (
|
||||
<ListBlockItem
|
||||
item={block}
|
||||
onSelectBlock={() => {
|
||||
if (!item) return;
|
||||
onSelect(item, block.id);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[item, onSelect]
|
||||
);
|
||||
|
||||
const renderInternalLink = React.useCallback(
|
||||
(link: {
|
||||
blockId: string;
|
||||
highlightedText: [TextSlice, TextSlice, TextSlice][];
|
||||
}) => (
|
||||
<ListNoteInternalLink
|
||||
link={link}
|
||||
onSelect={() => {
|
||||
if (!item) return;
|
||||
onSelect(item, link.blockId);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[item, onSelect]
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
type={"gray"}
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
onSelect(item as Note);
|
||||
}}
|
||||
customStyle={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
height: 45
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={SIZE.xl}
|
||||
onPress={() => {
|
||||
if (!item?.id) return;
|
||||
useExpandedStore.getState().setExpanded(item?.id);
|
||||
}}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
right={0}
|
||||
customStyle={{
|
||||
width: 35,
|
||||
height: 35
|
||||
}}
|
||||
name={expanded ? "chevron-down" : "chevron-right"}
|
||||
/>
|
||||
<Paragraph numberOfLines={1}>{item?.title}</Paragraph>
|
||||
</PressableButton>
|
||||
|
||||
{expanded && !item?.locked ? (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator color={colors.primary.accent} size={SIZE.lg} />
|
||||
) : (
|
||||
<>
|
||||
{listType === "linkedNotes" ? (
|
||||
<>
|
||||
{linkedBlocks.length === 0 ? (
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
No blocks linked
|
||||
</Paragraph>
|
||||
) : (
|
||||
linkedBlocks.map(renderBlock)
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{noteInternalLinks.length === 0 ? (
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
No references found of this note
|
||||
</Paragraph>
|
||||
) : (
|
||||
noteInternalLinks.map(renderInternalLink)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
type ReferencesListProps = {
|
||||
item: { id: string; type: string };
|
||||
close?: (ctx?: any) => void;
|
||||
};
|
||||
|
||||
export const ReferencesList = ({ item, close }: ReferencesListProps) => {
|
||||
const [tab, setTab] = useState(0);
|
||||
const updater = useRelationStore((state) => state.updater);
|
||||
const { colors } = useThemeColors();
|
||||
const [items, setItems] = useState<VirtualizedGrouping<Note>>();
|
||||
const hasNoRelations = !items || items?.placeholders?.length === 0;
|
||||
const internalLinks = useRef<InternalLink<"note">[]>();
|
||||
|
||||
useEffect(() => {
|
||||
db.relations?.[tab === 0 ? "from" : "to"]?.(
|
||||
{ id: item?.id, type: item?.type } as ItemReference,
|
||||
"note"
|
||||
)
|
||||
.selector.sorted({
|
||||
sortBy: "dateEdited",
|
||||
sortDirection: "desc"
|
||||
})
|
||||
.then((items) => {
|
||||
setItems(items);
|
||||
});
|
||||
}, [item?.id, item?.type, tab]);
|
||||
|
||||
const renderNote = React.useCallback(
|
||||
({ index }: any) => (
|
||||
<ListNoteItem
|
||||
id={index}
|
||||
items={items}
|
||||
onSelect={(note, blockId) => {
|
||||
console.log(note.id, blockId);
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note,
|
||||
blockId: blockId
|
||||
});
|
||||
tabBarRef.current?.goToPage(1);
|
||||
close?.();
|
||||
}}
|
||||
reference={item as Note}
|
||||
internalLinks={internalLinks}
|
||||
listType={tab === 0 ? "linkedNotes" : "referencedIn"}
|
||||
/>
|
||||
),
|
||||
[items, item, tab, close]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ height: "100%" }}>
|
||||
<SheetProvider context="local" />
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type={"gray"}
|
||||
title="Linked notes"
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
borderBottomWidth: 3,
|
||||
borderColor: tab === 0 ? colors.primary.accent : "transparent",
|
||||
height: 40,
|
||||
width: "50%"
|
||||
}}
|
||||
onPress={() => {
|
||||
setTab(0);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type={"gray"}
|
||||
title="Referenced in"
|
||||
style={{
|
||||
width: "50%",
|
||||
borderRadius: 0,
|
||||
borderBottomWidth: 3,
|
||||
borderColor: tab === 1 ? colors.primary.accent : "transparent",
|
||||
height: 40
|
||||
}}
|
||||
onPress={() => {
|
||||
setTab(1);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{hasNoRelations ? (
|
||||
<View
|
||||
style={{
|
||||
height: "85%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
{tab === 1
|
||||
? "This note is not referenced in other notes."
|
||||
: "This note does not link to other notes."}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
flex: 1,
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
bounces={false}
|
||||
data={items.placeholders}
|
||||
renderItem={renderNote}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
ReferencesList.present = ({
|
||||
reference
|
||||
}: {
|
||||
reference: { id: string; type: string };
|
||||
}) => {
|
||||
presentSheet({
|
||||
component: (ref, close, update) => (
|
||||
<ReferencesList item={reference} close={close} />
|
||||
),
|
||||
onClose: () => {
|
||||
useExpandedStore.setState({
|
||||
expanded: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { VAULT_ERRORS } from "@notesnook/core/dist/api/vault";
|
||||
import {
|
||||
Color,
|
||||
ItemReference,
|
||||
Note,
|
||||
Notebook,
|
||||
Reminder,
|
||||
@@ -64,6 +65,7 @@ import { eOpenLoginDialog } from "../utils/events";
|
||||
import { deleteItems } from "../utils/functions";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
import { ReferencesList } from "../components/sheets/references";
|
||||
|
||||
export const useActions = ({
|
||||
close,
|
||||
@@ -801,7 +803,20 @@ export const useActions = ({
|
||||
icon: "history",
|
||||
func: openHistory
|
||||
},
|
||||
|
||||
{
|
||||
id: "copy-link",
|
||||
title: "Copy link",
|
||||
icon: "link",
|
||||
func: () => {
|
||||
Clipboard.setString(`nn://note/${item.id}`);
|
||||
ToastManager.show({
|
||||
heading: "Note link copied",
|
||||
message: `nn://note/${item.id}`,
|
||||
context: "local",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "reminders",
|
||||
title: "Reminders",
|
||||
@@ -909,6 +924,16 @@ export const useActions = ({
|
||||
title: "Add tags",
|
||||
icon: "pound",
|
||||
func: addTo
|
||||
},
|
||||
{
|
||||
id: "references",
|
||||
title: "References",
|
||||
icon: "vector-link",
|
||||
func: () => {
|
||||
ReferencesList.present({
|
||||
reference: item as ItemReference
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,5 +28,5 @@ const EditorMobileSourceUrl =
|
||||
* The url should be something like this: http://192.168.100.126:3000/index.html
|
||||
*/
|
||||
export const EDITOR_URI = __DEV__
|
||||
? "http://192.168.43.252:3000/index.html"
|
||||
? EditorMobileSourceUrl
|
||||
: EditorMobileSourceUrl;
|
||||
|
||||
@@ -29,6 +29,7 @@ import { Settings } from "./types";
|
||||
import { getResponse, randId, textInput } from "./utils";
|
||||
import { Note } from "@notesnook/core/dist/types";
|
||||
import { useTabStore } from "./use-tab-store";
|
||||
import type { LinkAttributes } from "@notesnook/editor/dist/extensions/link";
|
||||
|
||||
type Action = { job: string; id: string };
|
||||
|
||||
@@ -322,6 +323,28 @@ const image = toBlobURL("${image.dataurl}", "${image.hash}");
|
||||
`);
|
||||
};
|
||||
|
||||
createInternalLink = async (
|
||||
attributes: LinkAttributes,
|
||||
resolverId: string
|
||||
) => {
|
||||
if (!resolverId) return;
|
||||
return this.doAsync(`
|
||||
if (globalThis.pendingResolvers["${resolverId}"]) {
|
||||
globalThis.pendingResolvers["${resolverId}"](${JSON.stringify(
|
||||
attributes
|
||||
)});
|
||||
}`);
|
||||
};
|
||||
|
||||
dismissCreateInternalLinkRequest = async (resolverId: string) => {
|
||||
if (!resolverId) return;
|
||||
return this.doAsync(`
|
||||
if (globalThis.pendingResolvers["${resolverId}"]) {
|
||||
globalThis.pendingResolvers["${resolverId}"](undefined);
|
||||
}
|
||||
`);
|
||||
};
|
||||
|
||||
scrollIntoViewById = async (id: string) => {
|
||||
const tabId = useTabStore.getState().currentTab;
|
||||
return this.doAsync(`
|
||||
|
||||
@@ -41,5 +41,6 @@ export const EventTypes = {
|
||||
tabsChanged: "editor-events:tabs-changed",
|
||||
showTabs: "editor-events:show-tabs",
|
||||
tabFocused: "editor-events:tab-focused",
|
||||
toc: "editor-events:toc"
|
||||
toc: "editor-events:toc",
|
||||
createInternalLink: "editor-events:create-internal-link"
|
||||
};
|
||||
|
||||
@@ -71,6 +71,8 @@ import { EditorMessage, EditorProps, useEditorType } from "./types";
|
||||
import { useTabStore } from "./use-tab-store";
|
||||
import { EditorEvents, editorState } from "./utils";
|
||||
import TableOfContents from "../../../components/sheets/toc";
|
||||
import LinkNote from "../../../components/sheets/link-note";
|
||||
import { parseInternalLink } from "@notesnook/core";
|
||||
|
||||
const publishNote = async () => {
|
||||
const user = useUserStore.getState().user;
|
||||
@@ -504,7 +506,35 @@ export const useEditorEvents = (
|
||||
eSendEvent(eOpenFullscreenEditor);
|
||||
break;
|
||||
case EventTypes.link:
|
||||
openLinkInBrowser(editorMessage.value as string);
|
||||
if (editorMessage.value.startsWith("nn://")) {
|
||||
const data = parseInternalLink(editorMessage.value);
|
||||
if (!data?.id) break;
|
||||
if (
|
||||
data.id ===
|
||||
useTabStore
|
||||
.getState()
|
||||
.getNoteIdForTab(useTabStore.getState().currentTab)
|
||||
) {
|
||||
if (data.params?.blockId) {
|
||||
setTimeout(() => {
|
||||
if (!data.params?.blockId) return;
|
||||
editor.commands.scrollIntoViewById(data.params.blockId);
|
||||
}, 150);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: await db.notes.note(data?.id),
|
||||
blockId: data.params?.blockId
|
||||
});
|
||||
console.log(
|
||||
"Opening note from internal link:",
|
||||
editorMessage.value
|
||||
);
|
||||
} else {
|
||||
openLinkInBrowser(editorMessage.value as string);
|
||||
}
|
||||
break;
|
||||
|
||||
case EventTypes.previewAttachment: {
|
||||
@@ -576,6 +606,13 @@ export const useEditorEvents = (
|
||||
|
||||
break;
|
||||
}
|
||||
case EventTypes.createInternalLink: {
|
||||
LinkNote.present(
|
||||
editorMessage.value.attributes,
|
||||
editorMessage.value.resolverId
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -132,6 +132,7 @@ export const useEditor = (
|
||||
const currentLoadingNoteId = useRef<string>();
|
||||
const loadingState = useRef<string>();
|
||||
const lastTabFocused = useRef(0);
|
||||
const blockIdRef = useRef<string>();
|
||||
const postMessage = useCallback(
|
||||
async <T>(type: string, data: T, tabId?: number, waitFor = 300) =>
|
||||
await post(
|
||||
@@ -382,7 +383,9 @@ export const useEditor = (
|
||||
forced?: boolean;
|
||||
newNote?: boolean;
|
||||
tabId?: number;
|
||||
blockId?: string;
|
||||
}) => {
|
||||
blockIdRef.current = event.blockId;
|
||||
state.current.currentlyEditing = true;
|
||||
if (
|
||||
!state.current.ready &&
|
||||
@@ -495,6 +498,13 @@ export const useEditor = (
|
||||
10000
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
if (blockIdRef.current) {
|
||||
commands.scrollIntoViewById(blockIdRef.current);
|
||||
blockIdRef.current = undefined;
|
||||
}
|
||||
}, 300);
|
||||
|
||||
loadingState.current = undefined;
|
||||
await commands.setTags(item);
|
||||
commands.setSettings();
|
||||
@@ -743,7 +753,7 @@ export const useEditor = (
|
||||
restoreTabNote();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}, [
|
||||
onReady,
|
||||
postMessage,
|
||||
|
||||
@@ -29,6 +29,7 @@ import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotes } from "../../stores/use-notes-store";
|
||||
import { openEditor } from "../notes/common";
|
||||
import LinkNote from "../../components/sheets/link-note";
|
||||
|
||||
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
const [notes, loading] = useNotes();
|
||||
@@ -79,7 +80,12 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
loading: "Loading your notes"
|
||||
}}
|
||||
/>
|
||||
<FloatingButton title="Create a new note" onPress={openEditor} />
|
||||
<FloatingButton
|
||||
title="Create a new note"
|
||||
onPress={() => {
|
||||
LinkNote.present();
|
||||
}}
|
||||
/>
|
||||
</DelayLayout>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import 'react-native-url-polyfill/auto';
|
||||
import "./polyfills/console-time.js"
|
||||
global.Buffer = require('buffer').Buffer;
|
||||
import '../app/common/logger/index';
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
"react-native-quick-sqlite": "^8.0.6",
|
||||
"react-native-theme-switch-animation": "^0.6.0",
|
||||
"@ammarahmed/react-native-background-fetch": "^4.2.2",
|
||||
"react-native-image-crop-picker": "^0.40.2"
|
||||
"react-native-image-crop-picker": "^0.40.2",
|
||||
"react-native-url-polyfill": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
||||
52361
apps/mobile/package-lock.json
generated
52361
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,8 +35,9 @@
|
||||
"@notesnook/editor": "file:../../packages/editor",
|
||||
"@notesnook/editor-mobile": "file:../../packages/editor-mobile",
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/theme": "file:../../packages/theme",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ const EXTRA_ICON_NAMES = [
|
||||
"cloud",
|
||||
"restore",
|
||||
"keyboard",
|
||||
"numeric"
|
||||
"numeric",
|
||||
"vector-link"
|
||||
];
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
||||
7
apps/theme-builder/package-lock.json
generated
7
apps/theme-builder/package-lock.json
generated
@@ -977,7 +977,7 @@
|
||||
},
|
||||
"../web": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.0.2-beta",
|
||||
"version": "3.0.6-beta",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
@@ -1020,6 +1020,7 @@
|
||||
"cronosjs": "^1.7.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"dayjs": "1.11.9",
|
||||
"diffblazer": "^1.0.1",
|
||||
"electron-trpc": "0.5.2",
|
||||
"event-source-polyfill": "^1.0.25",
|
||||
"fflate": "^0.8.0",
|
||||
@@ -1038,11 +1039,13 @@
|
||||
"platform": "^1.3.6",
|
||||
"qclone": "^1.2.0",
|
||||
"react": "18.2.0",
|
||||
"react-avatar-editor": "^13.0.2",
|
||||
"react-complex-tree": "^2.2.4",
|
||||
"react-day-picker": "^8.9.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-error-boundary": "^4.0.12",
|
||||
"react-freeze": "^1.0.3",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-loading-skeleton": "^3.3.1",
|
||||
"react-modal": "3.16.1",
|
||||
@@ -1068,8 +1071,10 @@
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"@types/platform": "^1.3.4",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-avatar-editor": "^13.0.2",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/react-modal": "3.16.3",
|
||||
"@types/react-scroll-sync": "^0.9.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/wicg-file-system-access": "^2020.9.6",
|
||||
"@vitejs/plugin-react-swc": "3.3.2",
|
||||
|
||||
6
packages/common/package-lock.json
generated
6
packages/common/package-lock.json
generated
@@ -36,6 +36,9 @@
|
||||
"@streetwriters/showdown": "^3.0.5-alpha",
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "1.11.9",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.1.0",
|
||||
"entities": "^4.3.1",
|
||||
"fuzzyjs": "^5.0.1",
|
||||
"html-to-text": "^9.0.5",
|
||||
@@ -176,6 +179,9 @@
|
||||
"bson-objectid": "^2.0.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "1.11.9",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.1.0",
|
||||
"dotenv": "^16.0.1",
|
||||
"entities": "^4.3.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
useTabStore
|
||||
} from "../hooks/useTabStore";
|
||||
import { EmotionEditorToolbarTheme } from "../theme-factory";
|
||||
import { EventTypes, Settings } from "../utils";
|
||||
import { EventTypes, randId, Settings } from "../utils";
|
||||
import Header from "./header";
|
||||
import StatusBar from "./statusbar";
|
||||
import Tags from "./tags";
|
||||
@@ -82,15 +82,15 @@ const Tiptap = ({
|
||||
transaction.getMeta("ignoreEdit")
|
||||
);
|
||||
},
|
||||
onOpenAttachmentPicker: (editor, type) => {
|
||||
openAttachmentPicker: (type) => {
|
||||
globalThis.editorControllers[tab.id]?.openFilePicker(type);
|
||||
return true;
|
||||
},
|
||||
onDownloadAttachment: (editor, attachment) => {
|
||||
downloadAttachment: (attachment) => {
|
||||
globalThis.editorControllers[tab.id]?.downloadAttachment(attachment);
|
||||
return true;
|
||||
},
|
||||
onPreviewAttachment(editor, attachment) {
|
||||
previewAttachment(attachment) {
|
||||
globalThis.editorControllers[tab.id]?.previewAttachment(attachment);
|
||||
return true;
|
||||
},
|
||||
@@ -99,6 +99,23 @@ const Tiptap = ({
|
||||
attachment
|
||||
) as Promise<string | undefined>;
|
||||
},
|
||||
createInternalLink(attributes) {
|
||||
logger("info", "create internal link");
|
||||
return new Promise((resolve) => {
|
||||
const id = randId("createInternalLink");
|
||||
|
||||
globalThis.pendingResolvers[id] = (value) => {
|
||||
delete globalThis.pendingResolvers[id];
|
||||
resolve(value);
|
||||
logger("info", "resolved create link request:", id);
|
||||
};
|
||||
|
||||
post("editor-events:create-internal-link", {
|
||||
attributes: attributes,
|
||||
resolverId: id
|
||||
});
|
||||
});
|
||||
},
|
||||
element: getContentDiv(),
|
||||
editable: !settings.readonly,
|
||||
editorProps: {
|
||||
@@ -107,7 +124,7 @@ const Tiptap = ({
|
||||
content: globalThis.editorControllers[tab.id]?.content?.current,
|
||||
isMobile: true,
|
||||
doubleSpacedLines: settings.doubleSpacedLines,
|
||||
onOpenLink: (url) => {
|
||||
openLink: (url) => {
|
||||
return globalThis.editorControllers[tab.id]?.openLink(url) || true;
|
||||
},
|
||||
copyToClipboard: (text) => {
|
||||
|
||||
@@ -17,13 +17,7 @@ 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 {
|
||||
ControlledMenu,
|
||||
// applyStatics
|
||||
MenuItem as MenuItemInner,
|
||||
SubMenu as SubMenuInner,
|
||||
MenuDivider
|
||||
} from "@szhsin/react-menu";
|
||||
import { ControlledMenu, MenuItem as MenuItemInner } from "@szhsin/react-menu";
|
||||
import ArrowBackIcon from "mdi-react/ArrowBackIcon";
|
||||
import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
|
||||
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
|
||||
@@ -32,12 +26,13 @@ import DotsHorizontalIcon from "mdi-react/DotsHorizontalIcon";
|
||||
import DotsVerticalIcon from "mdi-react/DotsVerticalIcon";
|
||||
import FullscreenIcon from "mdi-react/FullscreenIcon";
|
||||
import MagnifyIcon from "mdi-react/MagnifyIcon";
|
||||
import TableOfContentsIcon from "mdi-react/TableOfContentsIcon";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useSafeArea } from "../hooks/useSafeArea";
|
||||
import { useTabContext, useTabStore } from "../hooks/useTabStore";
|
||||
import { EventTypes, Settings } from "../utils";
|
||||
import styles from "./styles.module.css";
|
||||
import TableOfContentsIcon from "mdi-react/TableOfContentsIcon";
|
||||
|
||||
const menuClassName = ({ state }: any) =>
|
||||
state === "opening"
|
||||
? styles.menuOpening
|
||||
@@ -52,22 +47,10 @@ const menuItemClassName = ({ hover, disabled }: any) =>
|
||||
? styles.menuItemHover
|
||||
: styles.menuItem;
|
||||
|
||||
const submenuItemClassName = (modifiers: any) =>
|
||||
`${styles.submenuItem} ${menuItemClassName(modifiers)}`;
|
||||
|
||||
const MenuItem = (props: any) => (
|
||||
<MenuItemInner {...props} className={menuItemClassName} />
|
||||
);
|
||||
|
||||
const SubMenu = (props: any) => (
|
||||
<SubMenuInner
|
||||
{...props}
|
||||
menuClassName={menuClassName}
|
||||
itemProps={{ className: submenuItemClassName }}
|
||||
offsetY={-7}
|
||||
/>
|
||||
);
|
||||
|
||||
const Button = ({
|
||||
onPress,
|
||||
children,
|
||||
@@ -178,65 +161,6 @@ function Header({
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.undo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowULeftTopIcon
|
||||
color={
|
||||
!hasUndo
|
||||
? "var(--nn_secondary_background)"
|
||||
: "var(--nn_primary_paragraph)"
|
||||
}
|
||||
size={25 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.redo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowURightTopIcon
|
||||
color={
|
||||
!hasRedo
|
||||
? "var(--nn_secondary_background)"
|
||||
: "var(--nn_primary_paragraph)"
|
||||
}
|
||||
size={25 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
{!settings.premium && (
|
||||
<Button
|
||||
onPress={() => {
|
||||
@@ -266,32 +190,6 @@ function Header({
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.startSearch();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<MagnifyIcon
|
||||
size={28 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_primary_paragraph)"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
|
||||
<Button
|
||||
onPress={() => {
|
||||
@@ -342,8 +240,9 @@ function Header({
|
||||
<div
|
||||
style={{
|
||||
border: "2.5px solid var(--nn_primary_icon)",
|
||||
width: 20 * settings.fontScale,
|
||||
height: 20 * settings.fontScale,
|
||||
width: 19 * settings.fontScale,
|
||||
height: 19 * settings.fontScale,
|
||||
minWidth: 19 * settings.fontScale,
|
||||
borderRadius: 5,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
@@ -417,6 +316,102 @@ function Header({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.undo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowULeftTopIcon
|
||||
color={
|
||||
!hasUndo
|
||||
? "var(--nn_secondary_border)"
|
||||
: "var(--nn_primary_paragraph)"
|
||||
}
|
||||
size={25 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.redo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowURightTopIcon
|
||||
color={
|
||||
!hasRedo
|
||||
? "var(--nn_secondary_border)"
|
||||
: "var(--nn_primary_paragraph)"
|
||||
}
|
||||
size={25 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.startSearch();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_primary_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<MagnifyIcon
|
||||
size={28 * settings.fontScale}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_primary_paragraph)"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<MenuItem
|
||||
value="toc"
|
||||
style={{
|
||||
|
||||
@@ -271,8 +271,8 @@ export function useEditorController({
|
||||
case "native:status":
|
||||
break;
|
||||
case "native:keyboardShown":
|
||||
if (editor?.current) {
|
||||
scrollIntoView(editor?.current as any);
|
||||
if (editor) {
|
||||
scrollIntoView(editor as any);
|
||||
}
|
||||
break;
|
||||
case "native:attachment-data":
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Dispatch, MutableRefObject, RefObject, SetStateAction } from "react";
|
||||
import { EditorController } from "../hooks/useEditorController";
|
||||
|
||||
globalThis.sessionId = "notesnook-editor";
|
||||
globalThis.pendingResolvers = {};
|
||||
|
||||
globalThis.pendingResolvers = {};
|
||||
export function randId(prefix: string) {
|
||||
@@ -80,6 +81,7 @@ declare global {
|
||||
var noToolbar: boolean;
|
||||
var noHeader: boolean;
|
||||
function toBlobURL(dataurl: string, id?: string): string | undefined;
|
||||
var pendingResolvers: { [name: string]: (value: any) => void };
|
||||
/**
|
||||
* Id of current session
|
||||
*/
|
||||
@@ -187,9 +189,16 @@ export const EventTypes = {
|
||||
tabsChanged: "editor-events:tabs-changed",
|
||||
showTabs: "editor-events:show-tabs",
|
||||
tabFocused: "editor-events:tab-focused",
|
||||
toc: "editor-events:toc"
|
||||
toc: "editor-events:toc",
|
||||
createInternalLink: "editor-events:create-internal-link"
|
||||
} as const;
|
||||
|
||||
export function randId(prefix: string) {
|
||||
return Math.random()
|
||||
.toString(36)
|
||||
.replace("0.", prefix || "");
|
||||
}
|
||||
|
||||
export function isReactNative(): boolean {
|
||||
return !!window.ReactNativeWebView;
|
||||
}
|
||||
|
||||
3
packages/editor/package-lock.json
generated
3
packages/editor/package-lock.json
generated
@@ -105,6 +105,9 @@
|
||||
"@streetwriters/showdown": "^3.0.5-alpha",
|
||||
"async-mutex": "^0.3.2",
|
||||
"dayjs": "1.11.9",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.1.0",
|
||||
"entities": "^4.3.1",
|
||||
"fuzzyjs": "^5.0.1",
|
||||
"html-to-text": "^9.0.5",
|
||||
|
||||
Reference in New Issue
Block a user