mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
mobile: update ui
This commit is contained in:
@@ -78,7 +78,7 @@ export default function DatePickerComponent(props: {
|
||||
|
||||
<Button
|
||||
title={strings.cancel()}
|
||||
type="secondary"
|
||||
type="plain-outline"
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
|
||||
@@ -166,6 +166,13 @@ export const Header = ({
|
||||
>
|
||||
{rightButton ? (
|
||||
<IconButton {...rightButton} color={colors.primary.icon} />
|
||||
) : !hasSearch ? (
|
||||
<View
|
||||
style={{
|
||||
width: 20,
|
||||
height: 20
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{hasSearch ? (
|
||||
|
||||
@@ -17,420 +17,312 @@ 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 { useIsFeatureAvailable } from "@notesnook/common";
|
||||
import {
|
||||
ContentBlock,
|
||||
Note,
|
||||
VirtualizedGrouping,
|
||||
createInternalLink
|
||||
} from "@notesnook/core";
|
||||
import type { LinkAttributes } from "@notesnook/editor";
|
||||
import { ContentBlock, Note, createInternalLink } from "@notesnook/core";
|
||||
import { NativeEvents } from "@notesnook/editor-mobile/src/utils/native-events";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { ActivityIndicator, TextInput, View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useWindowDimensions, View } from "react-native";
|
||||
import { ScrollView } from "react-native-actions-sheet";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
import { db } from "../../../common/database";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { editorController } from "../../../screens/editor/tiptap/utils";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { presentSheet, ToastManager } from "../../../services/event-manager";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import AppIcon from "../../ui/AppIcon";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
|
||||
const ListNoteItem = ({
|
||||
id,
|
||||
items,
|
||||
onSelectNote
|
||||
}: {
|
||||
id: any;
|
||||
items: VirtualizedGrouping<Note> | undefined;
|
||||
onSelectNote: any;
|
||||
}) => {
|
||||
const [item] = useDBItem(id, "note", items);
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
onSelectNote(item as Note);
|
||||
}}
|
||||
type={"transparent"}
|
||||
style={{
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
height: 50
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Paragraph numberOfLines={1}>{item?.title}</Paragraph>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
type LinkMode = "note" | "paragraphs";
|
||||
|
||||
const ListBlockItem = ({
|
||||
item,
|
||||
onSelectBlock
|
||||
const ParagraphItem = ({
|
||||
block,
|
||||
selected,
|
||||
onSelect
|
||||
}: {
|
||||
item: ContentBlock;
|
||||
onSelectBlock: any;
|
||||
block: ContentBlock;
|
||||
selected: boolean;
|
||||
onSelect: (block: ContentBlock) => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const content =
|
||||
!block.content || block.content.trim() === ""
|
||||
? strings.linkNoteEmptyBlock()
|
||||
: block.content.length > 200
|
||||
? block.content.slice(0, 200) + "..."
|
||||
: block.content;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
onSelectBlock(item);
|
||||
}}
|
||||
type={"transparent"}
|
||||
type={selected ? "selected" : "transparent"}
|
||||
onPress={() => onSelect(block)}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
minHeight: 45
|
||||
padding: Spacing.LEVEL_2,
|
||||
borderRadius: Radius.S,
|
||||
borderWidth: selected ? 0 : 1,
|
||||
borderColor: colors.secondary.border,
|
||||
alignItems: "flex-start"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
columnGap: 10,
|
||||
alignItems: "flex-start",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
<Paragraph
|
||||
fontSize="SM"
|
||||
numberOfLines={2}
|
||||
color={selected ? colors.primary.heading : colors.primary.paragraph}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
{item?.content.length > 200
|
||||
? item?.content.slice(0, 200) + "..."
|
||||
: !item.content || item.content.trim() === ""
|
||||
? strings.linkNoteEmptyBlock()
|
||||
: item.content}
|
||||
</Paragraph>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: defaultBorderRadius,
|
||||
backgroundColor: colors.secondary.background,
|
||||
height: 25,
|
||||
minWidth: 25,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={AppFontSize.xs}>
|
||||
{item.type.toUpperCase()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
{content}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
export default function LinkNote(props: {
|
||||
attributes: LinkAttributes;
|
||||
note: Note;
|
||||
resolverId: string;
|
||||
onLinkCreated: () => void;
|
||||
close?: (ctx?: string) => void;
|
||||
}) {
|
||||
const blockLinking = useIsFeatureAvailable("blockLinking");
|
||||
const { note, resolverId } = props;
|
||||
const { colors } = useThemeColors();
|
||||
const query = useRef<string>(undefined);
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const nodesRef = useRef<ContentBlock[]>([]);
|
||||
const [nodes, setNodes] = useState<ContentBlock[]>([]);
|
||||
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
const [selectedNote, setSelectedNote] = useState<Note>();
|
||||
const [selectedNodeId, setSelectedNodeId] = useState<string>();
|
||||
const [blocksLoading, setBlocksLoading] = useState(false);
|
||||
const { height } = useWindowDimensions();
|
||||
const blockLinking = useIsFeatureAvailable("blockLinking");
|
||||
const [mode, setMode] = useState<LinkMode>("note");
|
||||
const [blocks, setBlocks] = useState<ContentBlock[]>([]);
|
||||
const [selectedBlockId, setSelectedBlockId] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
db.notes.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
|
||||
setNotes(notes);
|
||||
});
|
||||
}, []);
|
||||
db.notes.contentBlocks(note.id).then((result) => setBlocks(result));
|
||||
}, [note.id]);
|
||||
|
||||
const onChange = async (value: string) => {
|
||||
query.current = value;
|
||||
if (!selectedNote) {
|
||||
const notes = await db.lookup.notes(value).sorted();
|
||||
setNotes(notes);
|
||||
} else {
|
||||
if (value.startsWith("#")) {
|
||||
const headingNodes = nodesRef.current.filter((n) =>
|
||||
n.type.match(/(h1|h2|h3|h4|h5|h6)/g)
|
||||
);
|
||||
setNodes(
|
||||
headingNodes.filter((n) => n.content.includes(value.slice(1)))
|
||||
);
|
||||
} else {
|
||||
setNodes(nodesRef.current.filter((n) => n.content.includes(value)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onCreateLink = (blockId?: string) => {
|
||||
if (!selectedNote) return;
|
||||
const onAddLink = () => {
|
||||
const blockId = mode === "paragraphs" ? selectedBlockId : undefined;
|
||||
const link = createInternalLink(
|
||||
"note",
|
||||
selectedNote.id,
|
||||
blockId
|
||||
? {
|
||||
blockId: blockId
|
||||
}
|
||||
: undefined
|
||||
note.id,
|
||||
blockId ? { blockId } : undefined
|
||||
);
|
||||
editorController.current?.postMessage(NativeEvents.resolve, {
|
||||
data: {
|
||||
href: link,
|
||||
title: selectedNote.title
|
||||
title: note.title
|
||||
},
|
||||
resolverId: props.resolverId
|
||||
resolverId
|
||||
});
|
||||
};
|
||||
|
||||
const onSelectNote = async (note: Note) => {
|
||||
setSelectedNote(note);
|
||||
setBlocksLoading(true);
|
||||
inputRef.current?.clear();
|
||||
setTimeout(async () => {
|
||||
nodesRef.current = await db.notes.contentBlocks(note.id);
|
||||
setNodes(nodesRef.current);
|
||||
setBlocksLoading(false);
|
||||
});
|
||||
// Fetch and set note's nodes.
|
||||
};
|
||||
|
||||
const onSelectBlock = (block: ContentBlock) => {
|
||||
onCreateLink(block.id);
|
||||
props.onLinkCreated();
|
||||
props.close?.();
|
||||
props.onLinkCreated();
|
||||
ToastManager.show({
|
||||
message: strings.linkAdded(),
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingBottom: DefaultAppStyles.GAP,
|
||||
minHeight: "100%",
|
||||
maxHeight: "100%"
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopLeftRadius: 35,
|
||||
borderTopRightRadius: 35,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingTop: Spacing.LEVEL_2,
|
||||
paddingBottom: Spacing.LEVEL_2,
|
||||
gap: Spacing.LEVEL_4
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
alignItems: "flex-start",
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
placeholder={
|
||||
selectedNote
|
||||
? strings.searchSectionToLinkPlaceholder()
|
||||
: strings.searchNoteToLinkPlaceholder()
|
||||
}
|
||||
containerStyle={{
|
||||
width: "100%"
|
||||
}}
|
||||
marginBottom={0}
|
||||
onChangeText={(value) => {
|
||||
onChange(value);
|
||||
}}
|
||||
/>
|
||||
|
||||
{selectedNote ? (
|
||||
<View
|
||||
style={{
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph} size={AppFontSize.xs}>
|
||||
{strings.linkNoteSelectedNote()}
|
||||
</Paragraph>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
setSelectedNote(undefined);
|
||||
setSelectedNodeId(undefined);
|
||||
setNodes([]);
|
||||
}}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
height: 45,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.accent,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
type="secondaryAccented"
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{selectedNote?.title}
|
||||
</Paragraph>
|
||||
|
||||
<Paragraph
|
||||
color={colors.secondary.paragraph}
|
||||
size={AppFontSize.xs}
|
||||
>
|
||||
{strings.tapToDeselect()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
{nodes?.length > 0 ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
color={colors.secondary.paragraph}
|
||||
size={AppFontSize.xs}
|
||||
>
|
||||
{strings.linkNoteToSection()}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</View>
|
||||
) : null}
|
||||
<View style={{ gap: Spacing.LEVEL_1 }}>
|
||||
<Heading fontSize="XL" lineHeight="100%">
|
||||
{strings.linkOptions()}
|
||||
</Heading>
|
||||
<Paragraph fontSize="SM" color={colors.secondary.paragraph}>
|
||||
{strings.linkOptionsDesc()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
{selectedNote ? (
|
||||
<FlatList
|
||||
renderItem={({ item, index }) => (
|
||||
<ListBlockItem item={item} onSelectBlock={onSelectBlock} />
|
||||
)}
|
||||
<View style={{ gap: Spacing.LEVEL_2 }}>
|
||||
<Heading fontSize="LG" lineHeight="100%">
|
||||
{strings.selectedNoteLabel()}
|
||||
</Heading>
|
||||
<View
|
||||
style={{
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: Radius.XS,
|
||||
paddingHorizontal: Spacing.LEVEL_2,
|
||||
paddingVertical: Spacing.LEVEL_3
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
windowSize={3}
|
||||
keyExtractor={(item) => item.id}
|
||||
ListEmptyComponent={
|
||||
!blockLinking || blocksLoading ? (
|
||||
<ActivityIndicator size={25} color={colors.primary.accent} />
|
||||
) : !blockLinking.isAllowed ? (
|
||||
<View
|
||||
style={{
|
||||
gap: DefaultAppStyles.GAP_VERTICAL,
|
||||
backgroundColor: colors.secondary.background,
|
||||
padding: DefaultAppStyles.GAP,
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderWidth: 0.5,
|
||||
borderColor: colors.secondary.border,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
{blockLinking?.error}
|
||||
</Paragraph>
|
||||
<Button
|
||||
title={strings.upgradePlan()}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
type="accent"
|
||||
onPress={() => {
|
||||
Navigation.navigate("PayWall", {
|
||||
context: useUserStore.getState().user
|
||||
? "logged-in"
|
||||
: "logged-out",
|
||||
canGoBack: true
|
||||
});
|
||||
>
|
||||
<Paragraph
|
||||
color={colors.primary.heading}
|
||||
fontFamily="MEDIUM"
|
||||
fontSize="SM"
|
||||
>
|
||||
{note.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
props.close?.();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
data={blockLinking?.isAllowed ? nodes : []}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
padding: Spacing.LEVEL_1,
|
||||
borderRadius: Radius.S
|
||||
}}
|
||||
>
|
||||
{(
|
||||
[
|
||||
{ key: "note", label: strings.linkEntireNote() },
|
||||
{ key: "paragraphs", label: strings.specifyParagraphs() }
|
||||
] as { key: LinkMode; label: string }[]
|
||||
).map(({ key, label }) => {
|
||||
const active = mode === key;
|
||||
return (
|
||||
<Pressable
|
||||
key={key}
|
||||
type="transparent"
|
||||
onPress={() => setMode(key)}
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
borderRadius: Radius.XS,
|
||||
backgroundColor: active
|
||||
? colors.primary.background
|
||||
: "transparent",
|
||||
...(active ? getElevationStyle(2) : {})
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
fontSize="MD"
|
||||
style={{ textAlign: "center" }}
|
||||
color={
|
||||
active ? colors.primary.accent : colors.secondary.paragraph
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</Heading>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
{mode === "note" ? (
|
||||
<View style={{ gap: Spacing.LEVEL_1 }}>
|
||||
<Paragraph fontFamily="MEDIUM" fontSize="SM">
|
||||
{strings.linkedReferences()}
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_1,
|
||||
padding: Spacing.LEVEL_2,
|
||||
borderRadius: Radius.S,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.border
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: Radius.XS,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="file-text"
|
||||
iconFamily="notesnook"
|
||||
size={20}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
</View>
|
||||
<Heading fontSize="SM" style={{ flexShrink: 1 }}>
|
||||
{strings.entireNote()}
|
||||
</Heading>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
renderItem={({ item, index }: any) => (
|
||||
<ListNoteItem
|
||||
id={index}
|
||||
items={notes}
|
||||
onSelectNote={onSelectNote}
|
||||
/>
|
||||
<View style={{ gap: Spacing.LEVEL_3 }}>
|
||||
<Heading fontSize="LG" lineHeight="100%">
|
||||
{strings.selectParagraphs()}
|
||||
</Heading>
|
||||
{blockLinking?.isAllowed ? (
|
||||
<ScrollView
|
||||
nestedScrollEnabled
|
||||
keyboardShouldPersistTaps="handled"
|
||||
style={{ maxHeight: height * 0.4 }}
|
||||
contentContainerStyle={{ gap: Spacing.LEVEL_2 }}
|
||||
>
|
||||
{blocks.map((block) => (
|
||||
<ParagraphItem
|
||||
key={block.id}
|
||||
block={block}
|
||||
selected={selectedBlockId === block.id}
|
||||
onSelect={(b) => setSelectedBlockId(b.id)}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
padding: Spacing.LEVEL_3,
|
||||
borderRadius: Radius.S,
|
||||
borderWidth: 0.5,
|
||||
borderColor: colors.secondary.border,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
{blockLinking?.error}
|
||||
</Paragraph>
|
||||
<Button
|
||||
title={strings.upgradePlan()}
|
||||
style={{ width: "100%" }}
|
||||
type="accent"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
style={{
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
windowSize={3}
|
||||
data={notes?.placeholders}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{selectedNote ? (
|
||||
<Button
|
||||
style={{
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
title={strings.createLink()}
|
||||
type="accent"
|
||||
width="100%"
|
||||
onPress={() => {
|
||||
onCreateLink();
|
||||
props.onLinkCreated();
|
||||
props.close?.();
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
title={strings.addLink()}
|
||||
type="accent"
|
||||
disabled={
|
||||
mode === "paragraphs" &&
|
||||
selectedBlockId === undefined &&
|
||||
blocks.length !== 0
|
||||
}
|
||||
width="100%"
|
||||
onPress={onAddLink}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
LinkNote.present = (attributes: LinkAttributes, resolverId: string) => {
|
||||
let didCreateLink = false;
|
||||
LinkNote.present = (
|
||||
note: Note,
|
||||
resolverId: string,
|
||||
onLinkCreated: () => void
|
||||
) => {
|
||||
presentSheet({
|
||||
component: (ref, close) => (
|
||||
<LinkNote
|
||||
attributes={attributes}
|
||||
note={note}
|
||||
resolverId={resolverId}
|
||||
onLinkCreated={() => {
|
||||
didCreateLink = true;
|
||||
}}
|
||||
onLinkCreated={onLinkCreated}
|
||||
close={close}
|
||||
/>
|
||||
),
|
||||
onClose: () => {
|
||||
if (!didCreateLink) {
|
||||
editorController?.current.commands.dismissCreateInternalLinkRequest(
|
||||
resolverId
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1177,6 +1177,7 @@ export const useActions = ({
|
||||
title: strings.references(),
|
||||
icon: "link-alt",
|
||||
onPress: () => {
|
||||
close();
|
||||
Navigation.navigate("References", {
|
||||
reference: item as ItemReference
|
||||
});
|
||||
|
||||
@@ -307,6 +307,7 @@ let Attachments: any = null;
|
||||
let References: any = null;
|
||||
let NoteHistory: any = null;
|
||||
let NotePreview: any = null;
|
||||
let AddReference: any = null;
|
||||
export const RootNavigation = () => {
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
@@ -536,6 +537,15 @@ export const RootNavigation = () => {
|
||||
return NotePreview;
|
||||
}}
|
||||
/>
|
||||
|
||||
<RootStack.Screen
|
||||
name="AddReference"
|
||||
getComponent={() => {
|
||||
AddReference =
|
||||
AddReference || require("../screens/add-reference").default;
|
||||
return AddReference;
|
||||
}}
|
||||
/>
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
182
apps/mobile/app/screens/add-reference/index.tsx
Normal file
182
apps/mobile/app/screens/add-reference/index.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
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 { LegendList } from "@legendapp/list";
|
||||
import { Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { db } from "../../common/database";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import { Header } from "../../components/header";
|
||||
import LinkNote from "../../components/sheets/link-note";
|
||||
import Input from "../../components/ui/input";
|
||||
import { Pressable } from "../../components/ui/pressable";
|
||||
import LineSeparator from "../../components/ui/seperator/line-separator";
|
||||
import { TimeSince } from "../../components/ui/time-since";
|
||||
import Heading from "../../components/ui/typography/heading";
|
||||
import { useDBItem } from "../../hooks/use-db-item";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { editorController } from "../editor/tiptap/utils";
|
||||
|
||||
const NoteReferenceItem = ({
|
||||
id,
|
||||
items,
|
||||
onSelectNote
|
||||
}: {
|
||||
id: number;
|
||||
items?: VirtualizedGrouping<Note>;
|
||||
onSelectNote: (note: Note) => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(id, "note", items);
|
||||
|
||||
if (!item) {
|
||||
return <View style={{ height: 72, marginBottom: Spacing.LEVEL_2 }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_2,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.secondary.border,
|
||||
borderRadius: Radius.S,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_3,
|
||||
marginBottom: Spacing.LEVEL_2
|
||||
}}
|
||||
onPress={() => {
|
||||
onSelectNote(item);
|
||||
}}
|
||||
type="plain"
|
||||
customSelectedColor={colors.primary.shade}
|
||||
>
|
||||
<View style={{ flex: 1, gap: Spacing.LEVEL_0 }}>
|
||||
<Heading fontSize="MD" color={colors.primary.heading}>
|
||||
{item.title}
|
||||
</Heading>
|
||||
<TimeSince
|
||||
time={item.dateEdited}
|
||||
updateFrequency={60000}
|
||||
style={{
|
||||
fontSize: AppFontSize.xs,
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const AddReference = (props: NavigationProps<"AddReference">) => {
|
||||
const { resolverId } = props.route.params;
|
||||
const { colors } = useThemeColors();
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const didCreateLink = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
db.notes.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
|
||||
setNotes(notes);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (!didCreateLink.current) {
|
||||
editorController.current?.commands.dismissCreateInternalLinkRequest(
|
||||
resolverId
|
||||
);
|
||||
}
|
||||
};
|
||||
}, [resolverId]);
|
||||
|
||||
const onSearch = async (value: string) => {
|
||||
if (!value) {
|
||||
setNotes(await db.notes.all.sorted(db.settings.getGroupOptions("notes")));
|
||||
return;
|
||||
}
|
||||
setNotes(await db.lookup.notes(value).sorted());
|
||||
};
|
||||
|
||||
const onSelectNote = (note: Note) => {
|
||||
LinkNote.present(note, resolverId, () => {
|
||||
didCreateLink.current = true;
|
||||
Navigation.goBack();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{ flex: 1, backgroundColor: colors.primary.background }}
|
||||
>
|
||||
<Header
|
||||
renderedInRoute="AddReference"
|
||||
id="AddReference"
|
||||
title={strings.linkNote()}
|
||||
style={{ backgroundColor: colors.primary.background }}
|
||||
canGoBack
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingTop: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
placeholder={strings.searchNoteToLinkPlaceholder()}
|
||||
containerStyle={{ width: "100%" }}
|
||||
marginBottom={0}
|
||||
onChangeText={onSearch}
|
||||
button={{
|
||||
icon: "search",
|
||||
iconFamily: "notesnook",
|
||||
color: colors.secondary.icon,
|
||||
onPress: () => {}
|
||||
}}
|
||||
/>
|
||||
|
||||
<LineSeparator />
|
||||
|
||||
<LegendList
|
||||
data={notes?.placeholders || []}
|
||||
extraData={notes}
|
||||
estimatedItemSize={72}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
renderItem={({ index }) => (
|
||||
<NoteReferenceItem
|
||||
id={index}
|
||||
items={notes}
|
||||
onSelectNote={onSelectNote}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddReference;
|
||||
@@ -29,6 +29,8 @@ import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { db } from "../../common/database";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useArchived } from "../../stores/use-archived-store";
|
||||
import LineSeparator from "../../components/ui/seperator/line-separator";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
|
||||
export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
|
||||
const [archive, loading, refresh] = useArchived();
|
||||
@@ -53,6 +55,9 @@ export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
|
||||
canGoBack={false}
|
||||
hasSearch={true}
|
||||
id={route.name}
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_2
|
||||
}}
|
||||
onSearch={() => {
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(route.name),
|
||||
@@ -63,6 +68,7 @@ export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<LineSeparator paddingVertical={Spacing.LEVEL_3} />
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={archive}
|
||||
|
||||
@@ -44,7 +44,6 @@ import { AuthMode } from "../../../components/auth/common";
|
||||
import { Properties } from "../../../components/properties";
|
||||
import EditorTabs from "../../../components/sheets/editor-tabs";
|
||||
import { Issue } from "../../../components/sheets/github/issue";
|
||||
import LinkNote from "../../../components/sheets/link-note";
|
||||
import PaywallSheet from "../../../components/sheets/paywall";
|
||||
import TableOfContents from "../../../components/sheets/toc";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
@@ -297,7 +296,8 @@ export const useEditorEvents = (
|
||||
if (
|
||||
useNavigationStore.getState().currentRoute === "ManageTags" ||
|
||||
useNavigationStore.getState().currentRoute === "LinkNotebooks" ||
|
||||
useNavigationStore.getState().currentRoute === "AddReminder"
|
||||
useNavigationStore.getState().currentRoute === "AddReminder" ||
|
||||
useNavigationStore.getState().currentRoute === "AddReference"
|
||||
) {
|
||||
Navigation.goBack();
|
||||
} else {
|
||||
@@ -771,10 +771,9 @@ export const useEditorEvents = (
|
||||
break;
|
||||
}
|
||||
case EditorEvents.createInternalLink: {
|
||||
LinkNote.present(
|
||||
editorMessage.value.attributes,
|
||||
editorMessage.resolverId as string
|
||||
);
|
||||
Navigation.navigate("AddReference", {
|
||||
resolverId: editorMessage.resolverId as string
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import { useFavorites } from "../../stores/use-favorite-store";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { db } from "../../common/database";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import LineSeparator from "../../components/ui/seperator/line-separator";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
|
||||
export const Favorites = ({
|
||||
navigation,
|
||||
@@ -56,6 +58,9 @@ export const Favorites = ({
|
||||
canGoBack={false}
|
||||
hasSearch={true}
|
||||
id={route.name}
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_2
|
||||
}}
|
||||
onSearch={() => {
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(route.name),
|
||||
@@ -66,6 +71,9 @@ export const Favorites = ({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<LineSeparator paddingVertical={Spacing.LEVEL_3} />
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={favorites}
|
||||
|
||||
@@ -32,6 +32,8 @@ import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useTrash, useTrashStore } from "../../stores/use-trash-store";
|
||||
import SelectionHeader from "../../components/selection-header";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import LineSeparator from "../../components/ui/seperator/line-separator";
|
||||
|
||||
const onPressFloatingButton = () => {
|
||||
presentDialog({
|
||||
@@ -85,6 +87,9 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
id={route.name}
|
||||
canGoBack={false}
|
||||
hasSearch={true}
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_2
|
||||
}}
|
||||
onSearch={() => {
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(route.name),
|
||||
@@ -94,6 +99,7 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<LineSeparator paddingVertical={Spacing.LEVEL_3} />
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={trash}
|
||||
|
||||
@@ -77,7 +77,8 @@ const routeNames = {
|
||||
Attachments: "Attachments",
|
||||
References: "References",
|
||||
NoteHistory: "NoteHistory",
|
||||
NotePreview: "NotePreview"
|
||||
NotePreview: "NotePreview",
|
||||
AddReference: "AddReference"
|
||||
};
|
||||
|
||||
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
|
||||
|
||||
@@ -148,6 +148,9 @@ export interface RouteParams extends ParamListBase {
|
||||
note: TrashOrItem<Note>;
|
||||
session?: HistorySession & { session: string };
|
||||
};
|
||||
AddReference: {
|
||||
resolverId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type RouteName = keyof RouteParams;
|
||||
|
||||
@@ -760,6 +760,10 @@ msgstr "Add color"
|
||||
msgid "Add key"
|
||||
msgstr "Add key"
|
||||
|
||||
#: src/strings.ts:3001
|
||||
msgid "Add Link"
|
||||
msgstr "Add Link"
|
||||
|
||||
#: src/strings.ts:933
|
||||
msgid "Add notebook"
|
||||
msgstr "Add notebook"
|
||||
@@ -3035,6 +3039,10 @@ msgstr "Enter your recovery code."
|
||||
msgid "Enter your username"
|
||||
msgstr "Enter your username"
|
||||
|
||||
#: src/strings.ts:2992
|
||||
msgid "Entire Note"
|
||||
msgstr "Entire Note"
|
||||
|
||||
#: src/strings.ts:2487
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
@@ -4179,10 +4187,22 @@ msgstr "Line spacing changed"
|
||||
msgid "Link"
|
||||
msgstr "Link"
|
||||
|
||||
#: src/strings.ts:3002
|
||||
msgid "Link added"
|
||||
msgstr "Link added"
|
||||
|
||||
#: src/strings.ts:949
|
||||
msgid "Link copied"
|
||||
msgstr "Link copied"
|
||||
|
||||
#: src/strings.ts:2997
|
||||
msgid "Link entire note"
|
||||
msgstr "Link entire note"
|
||||
|
||||
#: src/strings.ts:2991
|
||||
msgid "Link note"
|
||||
msgstr "Link note"
|
||||
|
||||
#: src/strings.ts:969
|
||||
msgid "Link notebooks"
|
||||
msgstr "Link notebooks"
|
||||
@@ -4191,6 +4211,10 @@ msgstr "Link notebooks"
|
||||
msgid "Link notes"
|
||||
msgstr "Link notes"
|
||||
|
||||
#: src/strings.ts:2993
|
||||
msgid "Link options"
|
||||
msgstr "Link options"
|
||||
|
||||
#: src/strings.ts:2328
|
||||
msgid "Link settings"
|
||||
msgstr "Link settings"
|
||||
@@ -4219,6 +4243,10 @@ msgstr "Linked"
|
||||
msgid "Linked notes"
|
||||
msgstr "Linked notes"
|
||||
|
||||
#: src/strings.ts:2999
|
||||
msgid "Linked References"
|
||||
msgstr "Linked References"
|
||||
|
||||
#: src/strings.ts:1647
|
||||
msgid "Linking to a specific block is not available for locked notes."
|
||||
msgstr "Linking to a specific block is not available for locked notes."
|
||||
@@ -6601,6 +6629,10 @@ msgstr "Select notes to link to \"{title}\""
|
||||
msgid "Select nth day of the month to repeat the reminder."
|
||||
msgstr "Select nth day of the month to repeat the reminder."
|
||||
|
||||
#: src/strings.ts:3000
|
||||
msgid "Select paragraphs"
|
||||
msgstr "Select paragraphs"
|
||||
|
||||
#: src/strings.ts:2883
|
||||
msgid "Select Plan"
|
||||
msgstr "Select Plan"
|
||||
@@ -6625,6 +6657,10 @@ msgstr "Select the languages the spell checker should check in."
|
||||
msgid "Select the release track for Notesnook."
|
||||
msgstr "Select the release track for Notesnook."
|
||||
|
||||
#: src/strings.ts:2994
|
||||
msgid "Selected note"
|
||||
msgstr "Selected note"
|
||||
|
||||
#: src/strings.ts:236
|
||||
msgid "SELECTED NOTE"
|
||||
msgstr "SELECTED NOTE"
|
||||
@@ -6947,6 +6983,10 @@ msgstr "Spaces"
|
||||
msgid "Special Offer"
|
||||
msgstr "Special Offer"
|
||||
|
||||
#: src/strings.ts:2998
|
||||
msgid "Specify paragraphs"
|
||||
msgstr "Specify paragraphs"
|
||||
|
||||
#: src/strings.ts:2187
|
||||
msgid "Spell check"
|
||||
msgstr "Spell check"
|
||||
@@ -8353,6 +8393,10 @@ msgstr "You can create shortcuts of frequently accessed notebooks in the side me
|
||||
msgid "You can import your notes from most other note taking apps."
|
||||
msgstr "You can import your notes from most other note taking apps."
|
||||
|
||||
#: src/strings.ts:2996
|
||||
msgid "You can link the entire note or specific parts like paragraphs."
|
||||
msgstr "You can link the entire note or specific parts like paragraphs."
|
||||
|
||||
#: src/strings.ts:1482
|
||||
msgid "You can multi-select notes and move them to a notebook at once"
|
||||
msgstr "You can multi-select notes and move them to a notebook at once"
|
||||
|
||||
@@ -760,6 +760,10 @@ msgstr ""
|
||||
msgid "Add key"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:3001
|
||||
msgid "Add Link"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:933
|
||||
msgid "Add notebook"
|
||||
msgstr ""
|
||||
@@ -3024,6 +3028,10 @@ msgstr ""
|
||||
msgid "Enter your username"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2992
|
||||
msgid "Entire Note"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2487
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
@@ -4159,10 +4167,22 @@ msgstr ""
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:3002
|
||||
msgid "Link added"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:949
|
||||
msgid "Link copied"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2997
|
||||
msgid "Link entire note"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2991
|
||||
msgid "Link note"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:969
|
||||
msgid "Link notebooks"
|
||||
msgstr ""
|
||||
@@ -4171,6 +4191,10 @@ msgstr ""
|
||||
msgid "Link notes"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2993
|
||||
msgid "Link options"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2328
|
||||
msgid "Link settings"
|
||||
msgstr ""
|
||||
@@ -4199,6 +4223,10 @@ msgstr ""
|
||||
msgid "Linked notes"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2999
|
||||
msgid "Linked References"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1647
|
||||
msgid "Linking to a specific block is not available for locked notes."
|
||||
msgstr ""
|
||||
@@ -6573,7 +6601,11 @@ msgstr ""
|
||||
|
||||
#: src/strings.ts:355
|
||||
msgid "Select nth day of the month to repeat the reminder."
|
||||
msgstr "<<<<<<< HEAD<<<<<<< HEAD<<<<<<< HEAD"
|
||||
msgstr "<<<<<<< HEAD<<<<<<< HEAD<<<<<<< HEAD<<<<<<< HEAD"
|
||||
|
||||
#: src/strings.ts:3000
|
||||
msgid "Select paragraphs"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2883
|
||||
msgid "Select Plan"
|
||||
@@ -6599,6 +6631,10 @@ msgstr ""
|
||||
msgid "Select the release track for Notesnook."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2994
|
||||
msgid "Selected note"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:236
|
||||
msgid "SELECTED NOTE"
|
||||
msgstr ""
|
||||
@@ -6913,6 +6949,10 @@ msgstr ""
|
||||
msgid "Special Offer"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2998
|
||||
msgid "Specify paragraphs"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2187
|
||||
msgid "Spell check"
|
||||
msgstr ""
|
||||
@@ -8303,6 +8343,10 @@ msgstr ""
|
||||
msgid "You can import your notes from most other note taking apps."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2996
|
||||
msgid "You can link the entire note or specific parts like paragraphs."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1482
|
||||
msgid "You can multi-select notes and move them to a notebook at once"
|
||||
msgstr ""
|
||||
|
||||
@@ -2987,5 +2987,17 @@ Continue without attachments?`,
|
||||
reminderPlaceholder: () => t`Never miss a task. Set your first reminder now.`,
|
||||
emptyLinkedNotes: () => t`Links of notes that are references in this note.`,
|
||||
emptyReferences: () => t`Links of notes where this note is referenced.`,
|
||||
passwordProtection: () => t`Password protection (optional)`
|
||||
passwordProtection: () => t`Password protection (optional)`,
|
||||
linkNote: () => t`Link note`,
|
||||
entireNote: () => t`Entire Note`,
|
||||
linkOptions: () => t`Link options`,
|
||||
selectedNoteLabel: () => t`Selected note`,
|
||||
linkOptionsDesc: () =>
|
||||
t`You can link the entire note or specific parts like paragraphs.`,
|
||||
linkEntireNote: () => t`Link entire note`,
|
||||
specifyParagraphs: () => t`Specify paragraphs`,
|
||||
linkedReferences: () => t`Linked References`,
|
||||
selectParagraphs: () => t`Select paragraphs`,
|
||||
addLink: () => t`Add Link`,
|
||||
linkAdded: () => t`Link added`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user