mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 18:19:38 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4d7eac240 | ||
|
|
e7fb246c25 | ||
|
|
03defb3a48 | ||
|
|
73c44d476b | ||
|
|
0aaabfab62 |
@@ -22,7 +22,7 @@ import { Attachment, Note, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { RefObject, useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { db } from "../../common/database";
|
||||
@@ -59,6 +59,7 @@ import Paragraph from "../ui/typography/paragraph";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { createFormRef, validators } from "../ui/input/form-input";
|
||||
|
||||
const Actions = ({
|
||||
attachment,
|
||||
@@ -153,24 +154,48 @@ const Actions = ({
|
||||
{
|
||||
name: strings.rename(),
|
||||
onPress: () => {
|
||||
presentDialog({
|
||||
input: true,
|
||||
title: strings.renameFile(),
|
||||
defaultValue: attachment.filename,
|
||||
positivePress: async (value) => {
|
||||
if (value && value.length > 0) {
|
||||
await db.attachments.add({
|
||||
hash: attachment.hash,
|
||||
filename: value
|
||||
});
|
||||
setFilename(value);
|
||||
setAttachments();
|
||||
eSendEvent(eDBItemUpdate, attachment.id);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
positiveText: strings.rename()
|
||||
});
|
||||
close?.();
|
||||
setTimeout(() => {
|
||||
presentDialog({
|
||||
title: strings.renameFile(),
|
||||
form: {
|
||||
formRef: createFormRef({
|
||||
name: attachment.filename
|
||||
}),
|
||||
items: [
|
||||
{
|
||||
name: "name",
|
||||
defaultValue: attachment.filename,
|
||||
placeholder: strings.enterTitle(),
|
||||
ref: React.createRef<TextInput | null>(),
|
||||
validators: [validators.required(strings.nameIsRequired())]
|
||||
}
|
||||
],
|
||||
onFormSubmit: async (form) => {
|
||||
try {
|
||||
const value = form.getValue("name");
|
||||
await db.attachments.add({
|
||||
hash: attachment.hash,
|
||||
filename: value
|
||||
});
|
||||
setFilename(value);
|
||||
setAttachments();
|
||||
eSendEvent(eDBItemUpdate, attachment.id);
|
||||
ToastManager.show({
|
||||
message: `Attachment renamed to ${value}`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
form.setError("name", (e as Error).message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
positiveText: strings.rename()
|
||||
});
|
||||
}, 500);
|
||||
},
|
||||
icon: "form-textbox"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { ActivityIndicator, StyleSheet, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { getColorLinearShade } from "../../utils/colors";
|
||||
@@ -79,6 +79,11 @@ const DialogButtons = ({
|
||||
/>
|
||||
<Paragraph color={colors.primary.accent}>{" " + doneText}</Paragraph>
|
||||
</View>
|
||||
) : loading ? (
|
||||
<ActivityIndicator
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
) : (
|
||||
<View />
|
||||
)}
|
||||
@@ -105,7 +110,6 @@ const DialogButtons = ({
|
||||
style={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
loading={loading}
|
||||
bold
|
||||
type={positiveType || "transparent"}
|
||||
title={positiveTitle}
|
||||
|
||||
@@ -67,15 +67,13 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
// Handle form submission if form is available
|
||||
if (dialogInfo?.form && formRef.current) {
|
||||
inputRef.current?.blur();
|
||||
setLoading(true);
|
||||
try {
|
||||
const isValid = await formRef.current.validate();
|
||||
if (!isValid) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dialogInfo.form.onFormSubmit) {
|
||||
setLoading(true);
|
||||
const result = await dialogInfo.form.onFormSubmit(formRef.current);
|
||||
if (result === false) {
|
||||
setLoading(false);
|
||||
@@ -93,7 +91,7 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
let result = false;
|
||||
try {
|
||||
result = await dialogInfo.positivePress(
|
||||
values.current.inputValue || dialogInfo.defaultValue,
|
||||
values.current.inputValue,
|
||||
checked
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
@@ -368,7 +368,14 @@ export const useActions = ({
|
||||
inputPlaceholder: strings.name(),
|
||||
defaultValue: item.title,
|
||||
positivePress: async (value) => {
|
||||
if (!value || value.trim().length === 0) return;
|
||||
if (!value || value.trim().length === 0) {
|
||||
ToastManager.error(
|
||||
new Error(strings.nameIsRequired()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
await db.colors.add({
|
||||
id: item.id,
|
||||
title: value
|
||||
|
||||
@@ -47,6 +47,14 @@ export async function verifyUser(
|
||||
negativeText: closeText || strings.cancel(),
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
if (!value || !value.trim()) {
|
||||
ToastManager.error(
|
||||
new Error(strings.passwordNotEntered()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const user = await db.user.getUser();
|
||||
let verified = !user ? true : await db.user.verifyPassword(value);
|
||||
if (verified) {
|
||||
@@ -95,6 +103,14 @@ export async function verifyUserWithApplock() {
|
||||
keyboardType: keyboardType,
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
if (!value || !value.trim()) {
|
||||
ToastManager.error(
|
||||
new Error(strings.passwordNotEntered()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const verified = await validateAppLockPassword(value);
|
||||
if (!verified) {
|
||||
ToastManager.show({
|
||||
|
||||
@@ -529,6 +529,14 @@ export const settingsGroups: SettingSection[] = [
|
||||
positiveText: strings.delete(),
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
if (!value || !value.trim()) {
|
||||
ToastManager.error(
|
||||
new Error(strings.passwordNotEntered()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const verified = await db.user?.verifyPassword(value);
|
||||
if (verified) {
|
||||
setTimeout(async () => {
|
||||
|
||||
@@ -201,6 +201,14 @@ const SettingsUserSection = ({ item }) => {
|
||||
inputPlaceholder: strings.enterFullName(),
|
||||
defaultValue: userProfile?.fullName,
|
||||
positivePress: async (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
ToastManager.error(
|
||||
new Error(strings.nameIsRequired()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
db.settings
|
||||
.setProfile({
|
||||
fullName: value
|
||||
|
||||
@@ -65,6 +65,14 @@ export async function unlockVault({
|
||||
paragraph: paragraph,
|
||||
inputPlaceholder: strings.enterPassword(),
|
||||
positivePress: async (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
ToastManager.error(
|
||||
new Error(strings.passwordNotEntered()),
|
||||
undefined,
|
||||
"local"
|
||||
);
|
||||
return;
|
||||
}
|
||||
const unlocked = await db.vault.unlock(value);
|
||||
if (!unlocked) {
|
||||
ToastManager.show({
|
||||
|
||||
@@ -47,6 +47,7 @@ import { FlexScrollContainer } from "../../components/scroll-container";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
DropdownSettingComponent,
|
||||
Section,
|
||||
SectionGroup,
|
||||
SectionKeys,
|
||||
Setting,
|
||||
@@ -254,9 +255,15 @@ export const SettingsDialog = DialogManager.register(function SettingsDialog(
|
||||
overflow: "auto"
|
||||
}}
|
||||
>
|
||||
{activeSettings.map((group) => (
|
||||
<SettingsGroupComponent item={group} />
|
||||
))}
|
||||
{activeSettings.length > 0 ? (
|
||||
activeSettings.map((group) => (
|
||||
<SettingsGroupComponent item={group} />
|
||||
))
|
||||
) : (
|
||||
<Text variant="body" sx={{ color: "paragraph-secondary" }}>
|
||||
{strings.noResultsFound()}
|
||||
</Text>
|
||||
)}
|
||||
</FlexScrollContainer>
|
||||
</Flex>
|
||||
</Dialog>
|
||||
@@ -317,12 +324,17 @@ function SettingsSideBar(props: SettingsSideBarProps) {
|
||||
SettingsGroups.filter((g) => g.section === route)
|
||||
);
|
||||
|
||||
const groups: SettingsGroup[] = [];
|
||||
let groups: SettingsGroup[] = [];
|
||||
for (const group of SettingsGroups) {
|
||||
const section = findSection(group.section);
|
||||
if (section?.isHidden?.() || group.isHidden?.()) continue;
|
||||
|
||||
const isTitleMatch =
|
||||
typeof group.header === "string" &&
|
||||
group.header.toLowerCase().includes(query);
|
||||
const isSectionMatch = group.section.includes(query);
|
||||
const isSectionMatch = group.section
|
||||
.toLowerCase()
|
||||
.includes(query);
|
||||
|
||||
if (isTitleMatch || isSectionMatch) {
|
||||
groups.push(group);
|
||||
@@ -347,6 +359,18 @@ function SettingsSideBar(props: SettingsSideBarProps) {
|
||||
if (!settings.length) continue;
|
||||
groups.push({ ...group, settings });
|
||||
}
|
||||
const matchedSections = findSections(query);
|
||||
if (matchedSections.length > 0) {
|
||||
const matchedGroups = SettingsGroups.filter((g) =>
|
||||
matchedSections.some((s) => s.key === g.section)
|
||||
);
|
||||
// remove groups whose sections were matched to avoid duplicate
|
||||
// entries.
|
||||
groups = groups.filter(
|
||||
(g) => !matchedGroups.some((mg) => mg.section === g.section)
|
||||
);
|
||||
groups.push(...matchedGroups);
|
||||
}
|
||||
onNavigate(groups);
|
||||
}}
|
||||
/>
|
||||
@@ -717,3 +741,22 @@ function NumberInput({
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
function findSection(key: SectionKeys) {
|
||||
for (const group of sectionGroups) {
|
||||
const section = group.sections.find((s) => s.key === key);
|
||||
if (section) return section;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findSections(query: string) {
|
||||
const sections: Section[] = [];
|
||||
for (const group of sectionGroups) {
|
||||
for (const section of group.sections) {
|
||||
if (section.isHidden?.()) continue;
|
||||
if (section.title.toLowerCase().includes(query)) sections.push(section);
|
||||
}
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2026-04-20 11:33+0500\n"
|
||||
"POT-Creation-Date: 2026-05-08 10:43+0500\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
"Language: en\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/strings.ts:2421
|
||||
msgid " \"Notebook > Notes\""
|
||||
@@ -4034,6 +4028,10 @@ msgstr "Multi-layer encryption to most important notes"
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: src/strings.ts:2669
|
||||
msgid "Name is required."
|
||||
msgstr "Name is required."
|
||||
|
||||
#: src/strings.ts:1688
|
||||
msgid "Native high-performance encryption"
|
||||
msgstr "Native high-performance encryption"
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2026-04-20 11:33+0500\n"
|
||||
"POT-Creation-Date: 2026-05-08 10:43+0500\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
"Language: pseudo-LOCALE\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: src/strings.ts:2421
|
||||
msgid " \"Notebook > Notes\""
|
||||
@@ -4014,6 +4008,10 @@ msgstr ""
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2669
|
||||
msgid "Name is required."
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1688
|
||||
msgid "Native high-performance encryption"
|
||||
msgstr ""
|
||||
|
||||
@@ -2665,5 +2665,6 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
deleteAttachmentConfirm: () =>
|
||||
t`Are you sure you want to delete this attachment?`,
|
||||
attachmentDeleted: () => t`Attachment deleted`,
|
||||
titleIsRequired: () => t`Title is required`
|
||||
titleIsRequired: () => t`Title is required`,
|
||||
nameIsRequired: () => t`Name is required.`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user