diff --git a/apps/mobile/app/components/attachments/actions.tsx b/apps/mobile/app/components/attachments/actions.tsx index d8b198d7e..4ee2de59e 100644 --- a/apps/mobile/app/components/attachments/actions.tsx +++ b/apps/mobile/app/components/attachments/actions.tsx @@ -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(), + 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" }, diff --git a/apps/mobile/app/components/dialog/dialog-buttons.tsx b/apps/mobile/app/components/dialog/dialog-buttons.tsx index fd055abfd..01e8d1efb 100644 --- a/apps/mobile/app/components/dialog/dialog-buttons.tsx +++ b/apps/mobile/app/components/dialog/dialog-buttons.tsx @@ -20,7 +20,7 @@ along with this program. If not, see . 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 = ({ /> {" " + doneText} + ) : loading ? ( + ) : ( )} @@ -105,7 +110,6 @@ const DialogButtons = ({ style={{ marginLeft: 10 }} - loading={loading} bold type={positiveType || "transparent"} title={positiveTitle} diff --git a/apps/mobile/app/components/dialog/index.tsx b/apps/mobile/app/components/dialog/index.tsx index ed1b273b2..c4e6a1080 100644 --- a/apps/mobile/app/components/dialog/index.tsx +++ b/apps/mobile/app/components/dialog/index.tsx @@ -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) { diff --git a/apps/mobile/app/hooks/use-actions.tsx b/apps/mobile/app/hooks/use-actions.tsx index 2e0a685e0..74fc1daf9 100644 --- a/apps/mobile/app/hooks/use-actions.tsx +++ b/apps/mobile/app/hooks/use-actions.tsx @@ -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 diff --git a/apps/mobile/app/screens/settings/functions.js b/apps/mobile/app/screens/settings/functions.js index 4e03de847..b12e5e70c 100644 --- a/apps/mobile/app/screens/settings/functions.js +++ b/apps/mobile/app/screens/settings/functions.js @@ -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({ diff --git a/apps/mobile/app/screens/settings/settings-data.tsx b/apps/mobile/app/screens/settings/settings-data.tsx index 3a8089e2b..8c9b51509 100644 --- a/apps/mobile/app/screens/settings/settings-data.tsx +++ b/apps/mobile/app/screens/settings/settings-data.tsx @@ -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 () => { diff --git a/apps/mobile/app/screens/settings/user-section.jsx b/apps/mobile/app/screens/settings/user-section.jsx index f399ce717..b87cccfb7 100644 --- a/apps/mobile/app/screens/settings/user-section.jsx +++ b/apps/mobile/app/screens/settings/user-section.jsx @@ -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 diff --git a/apps/mobile/app/utils/unlock-vault.ts b/apps/mobile/app/utils/unlock-vault.ts index 75ae34d8d..92577f154 100644 --- a/apps/mobile/app/utils/unlock-vault.ts +++ b/apps/mobile/app/utils/unlock-vault.ts @@ -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({ diff --git a/packages/intl/locale/en.po b/packages/intl/locale/en.po index 6499ba0ea..534e7e949 100644 --- a/packages/intl/locale/en.po +++ b/packages/intl/locale/en.po @@ -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" diff --git a/packages/intl/locale/pseudo-LOCALE.po b/packages/intl/locale/pseudo-LOCALE.po index add9f914c..0ad2efe05 100644 --- a/packages/intl/locale/pseudo-LOCALE.po +++ b/packages/intl/locale/pseudo-LOCALE.po @@ -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 "" diff --git a/packages/intl/src/strings.ts b/packages/intl/src/strings.ts index 472ef36c3..6bfe069ec 100644 --- a/packages/intl/src/strings.ts +++ b/packages/intl/src/strings.ts @@ -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.` };