diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index b09ee995d..be041705d 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -140,7 +140,7 @@ android { if (project.hasProperty("prBuildNumber")) { versionCode Integer.parseInt(prBuildNumber()) } else { - versionCode 3103 + versionCode 3104 } versionName getNpmVersion() testBuildType System.getProperty('testBuildType', 'debug') diff --git a/apps/mobile/app/components/attachments/actions.tsx b/apps/mobile/app/components/attachments/actions.tsx index 811c08a0e..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,50 +154,100 @@ 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" }, { name: strings.delete(), onPress: async () => { - const relations = await db.relations.to(attachment, "note").get(); - await db.attachments.remove(attachment.hash, false); - setAttachments(); - eSendEvent(eDBItemUpdate, attachment.id); - relations - .map((relation) => relation.fromId) - .forEach(async (id) => { - useTabStore.getState().forEachNoteTab(id, async (tab) => { - const isFocused = useTabStore.getState().currentTab === tab.id; - if (isFocused) { - eSendEvent(eOnLoadNote, { - item: await db.notes.note(id), - forced: true - }); - } else { - editorController.current.commands.setLoading(true, tab.id); - } - }); - }); close?.(); + setTimeout(() => { + presentDialog({ + title: strings.deleteAttachment(), + paragraph: strings.deleteAttachmentConfirm(), + positiveText: strings.yes(), + negativeText: strings.no(), + positiveType: "errorShade", + positivePress: async () => { + try { + const relations = await db.relations + .to(attachment, "note") + .get(); + await db.attachments.remove(attachment.hash, false); + ToastManager.show({ + type: "success", + message: strings.attachmentDeleted() + }); + setAttachments(); + eSendEvent(eDBItemUpdate, attachment.id); + relations + .map((relation) => relation.fromId) + .forEach(async (id) => { + useTabStore.getState().forEachNoteTab(id, async (tab) => { + const isFocused = + useTabStore.getState().currentTab === tab.id; + if (isFocused) { + eSendEvent(eOnLoadNote, { + item: await db.notes.note(id), + forced: true + }); + } else { + editorController.current.commands.setLoading( + true, + tab.id + ); + } + }); + }); + return true; + } catch (e) { + return false; + } + } + }); + }, 500); }, icon: "delete-outline" } diff --git a/apps/mobile/app/components/auth/change-password.tsx b/apps/mobile/app/components/auth/change-password.tsx index a3ca00938..d10ae1341 100644 --- a/apps/mobile/app/components/auth/change-password.tsx +++ b/apps/mobile/app/components/auth/change-password.tsx @@ -18,6 +18,7 @@ along with this program. If not, see . */ import { strings } from "@notesnook/intl"; +import { useThemeColors } from "@notesnook/theme"; import React, { useRef, useState } from "react"; import { View } from "react-native"; import { db } from "../../common/database"; @@ -26,42 +27,44 @@ import { eSendEvent, ToastManager } from "../../services/event-manager"; import Navigation from "../../services/navigation"; import { useUserStore } from "../../stores/use-user-store"; import { eOpenRecoveryKeyDialog } from "../../utils/events"; +import { AppFontSize } from "../../utils/size"; import { DefaultAppStyles } from "../../utils/styles"; import { Dialog } from "../dialog"; +import AppIcon from "../ui/AppIcon"; import { Button } from "../ui/button"; -import Input from "../ui/input"; +import FormInput, { createFormRef, validators } from "../ui/input/form-input"; import { Notice } from "../ui/notice"; +import Paragraph from "../ui/typography/paragraph"; import { TextInput } from "react-native-gesture-handler"; export const ChangePassword = () => { - const passwordInputRef = useRef(null); - const password = useRef(undefined); + const { colors } = useThemeColors(); + const formRef = useRef( + createFormRef({ + oldPassword: "", + password: "" + }) + ); const oldPasswordInputRef = useRef(null); - const oldPassword = useRef(undefined); - - const [error, setError] = useState(false); + const passwordInputRef = useRef(null); const [loading, setLoading] = useState(false); + const [error, setError] = useState(); const user = useUserStore((state) => state.user); const changePassword = async () => { + setError(undefined); + formRef.current.clearErrors(); + if (!user?.isEmailConfirmed) { - ToastManager.show({ - heading: strings.emailNotConfirmed(), - message: strings.emailNotConfirmedDesc(), - type: "error", - context: "local" - }); + setError(strings.emailNotConfirmedDesc()); return; } - if (error || !oldPassword.current || !password.current) { - ToastManager.show({ - heading: strings.allFieldsRequired(), - message: strings.allFieldsRequiredDesc(), - type: "error", - context: "local" - }); + if (!formRef.current.validate()) { return; } + + const values = formRef.current.getValues(); + setLoading(true); try { const result = await BackupService.run( @@ -74,8 +77,8 @@ export const ChangePassword = () => { } const passwordChanged = await db.user.changePassword( - oldPassword.current, - password.current + values.oldPassword, + values.password ); if (!passwordChanged) { @@ -91,15 +94,15 @@ export const ChangePassword = () => { Navigation.goBack(); eSendEvent(eOpenRecoveryKeyDialog); } catch (e) { + const message = (e as Error).message; setLoading(false); - ToastManager.show({ - heading: strings.passwordChangeFailed(), - message: (e as Error).message, - type: "error", - context: "local" - }); + + if (/old password/i.test(message)) { + formRef.current.setError("oldPassword", message); + } else { + setError(message); + } } - setLoading(false); }; return ( @@ -110,36 +113,61 @@ export const ChangePassword = () => { }} > - { - oldPassword.current = value; - }} + loading={loading} + validators={[validators.required(strings.currentPasswordRequired())]} returnKeyLabel="Next" returnKeyType="next" secureTextEntry autoComplete="password" autoCapitalize="none" autoCorrect={false} - placeholder={strings.oldPassword()} + placeholder={strings.currentPassword()} + onSubmitEditing={() => { + passwordInputRef.current?.focus(); + }} /> - { - password.current = value; - }} - onErrorCheck={(e) => setError(e)} + loading={loading} + validators={[validators.required(strings.passwordRequired())]} returnKeyLabel={strings.next()} returnKeyType="next" secureTextEntry - validationType="password" autoComplete="password" autoCapitalize="none" autoCorrect={false} placeholder={strings.newPassword()} + onSubmitEditing={() => { + changePassword(); + }} /> + {error ? ( + {}} + color={colors.error.accent} + style={{ + textAlign: "center", + marginTop: DefaultAppStyles.GAP_VERTICAL + }} + > + {" "} + {error} + + ) : null} + diff --git a/apps/mobile/app/components/auth/forgot-password.tsx b/apps/mobile/app/components/auth/forgot-password.tsx index 85ced9c76..47d3836aa 100644 --- a/apps/mobile/app/components/auth/forgot-password.tsx +++ b/apps/mobile/app/components/auth/forgot-password.tsx @@ -27,7 +27,7 @@ import { useThemeColors } from "@notesnook/theme"; import DialogHeader from "../dialog/dialog-header"; import { Button } from "../ui/button"; import { IconButton } from "../ui/icon-button"; -import Input from "../ui/input"; +import FormInput, { createFormRef, validators } from "../ui/input/form-input"; import Seperator from "../ui/seperator"; import Heading from "../ui/typography/heading"; import Paragraph from "../ui/typography/paragraph"; @@ -36,21 +36,22 @@ import { DefaultAppStyles } from "../../utils/styles"; export const ForgotPassword = ({ userEmail }: { userEmail: string }) => { const { colors } = useThemeColors("sheet"); - const email = useRef(userEmail); + const formRef = useRef( + createFormRef({ + email: userEmail || "" + }) + ); const emailInputRef = useRef(null); - const [error, setError] = useState(false); const [loading, setLoading] = useState(false); const [sent, setSent] = useState(false); const sendRecoveryEmail = async () => { - if (!email.current || error) { - ToastManager.show({ - heading: strings.emailRequired(), - type: "error", - context: "local" - }); + if (formRef.current.validateField("email")) { return; } + + const values = formRef.current.getValues(); + setLoading(true); try { const lastRecoveryEmailTime = SettingsService.get().lastRecoveryEmailTime; @@ -60,7 +61,7 @@ export const ForgotPassword = ({ userEmail }: { userEmail: string }) => { ) { throw new Error(strings.pleaseWaitBeforeSendEmail()); } - await db.user.recoverAccount(email.current.toLowerCase()); + await db.user.recoverAccount(values.email.toLowerCase()); SettingsService.set({ lastRecoveryEmailTime: Date.now() }); @@ -75,12 +76,7 @@ export const ForgotPassword = ({ userEmail }: { userEmail: string }) => { setSent(true); } catch (e) { setLoading(false); - ToastManager.show({ - heading: strings.recoveryEmailFailed(), - message: (e as Error).message, - type: "error", - context: "local" - }); + formRef.current.setError("email", (e as Error).message); } }; @@ -126,22 +122,25 @@ export const ForgotPassword = ({ userEmail }: { userEmail: string }) => { - { - email.current = value; - }} - defaultValue={email.current} - onErrorCheck={(e) => setError(e)} + loading={loading} returnKeyLabel={strings.next()} returnKeyType="next" autoComplete="email" - validationType="email" + keyboardType="email-address" autoCorrect={false} autoCapitalize="none" - errorMessage={strings.emailInvalid()} placeholder={strings.email()} - onSubmit={() => {}} + validators={[ + validators.required(strings.emailRequired()), + validators.email(strings.enterAValidEmailAddress()) + ]} + onSubmitEditing={() => { + sendRecoveryEmail(); + }} /> @@ -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({ ); } + +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; +} diff --git a/apps/web/src/interfaces/fs.ts b/apps/web/src/interfaces/fs.ts index 7bc5cb340..fda669c2e 100644 --- a/apps/web/src/interfaces/fs.ts +++ b/apps/web/src/interfaces/fs.ts @@ -49,6 +49,7 @@ import { } from "@notesnook/core"; import { logger } from "../utils/logger"; import { newQueue } from "@henrygd/queue"; +import { strings } from "@notesnook/intl"; export const ABYTES = 17; const CHUNK_SIZE = 512 * 1024; @@ -499,12 +500,20 @@ async function downloadFile( { type: "download", hash: filename } ); - const signedUrl = ( - await axios.get(url, { - headers, - responseType: "text" - }) - ).data; + const signedUrlResponse = await axios + .get(url, { headers, responseType: "text" }) + .catch((e) => { + if (e.response?.status === 401) { + showToast("error", strings.pleaseLoginToDownloadAttachments()); + return null; + } + throw e; + }); + if (!signedUrlResponse) { + reportProgress(undefined, { type: "download", hash: filename }); + return false; + } + const signedUrl = signedUrlResponse.data; logger.debug("Got attachment signed url", { filename }); diff --git a/docs/help/contents/faqs/login-to-restore-attachments-in-backup.md b/docs/help/contents/faqs/login-to-restore-attachments-in-backup.md new file mode 100644 index 000000000..13bddecfb --- /dev/null +++ b/docs/help/contents/faqs/login-to-restore-attachments-in-backup.md @@ -0,0 +1,8 @@ +--- +title: Login to restore attachments in backup. +description: We require users to be logged in to restore attachments in backup. +--- + +# Login to restore attachments in backup. + +We require users to be logged in to restore attachments in backup. This is because attachments are encrypted using a sub-key derived from your database encryption key. Without a login, we cannot encrypt/upload/sync attachments. diff --git a/docs/help/docgen.yaml b/docs/help/docgen.yaml index bc5bf0299..639d7fac0 100644 --- a/docs/help/docgen.yaml +++ b/docs/help/docgen.yaml @@ -91,3 +91,4 @@ navigation: - path: faqs/what-are-merge-conflicts.md - path: faqs/is-there-an-eta.md - path: faqs/login-to-upload-attachments.md + - path: faqs/login-to-restore-attachments-in-backup.md diff --git a/fastlane/metadata/android/en-US/changelogs/15519.txt b/fastlane/metadata/android/en-US/changelogs/15519.txt new file mode 100644 index 000000000..3aaf71b15 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/15519.txt @@ -0,0 +1,3 @@ +- Bug fixes and improvements + +Thank you for using Notesnook! diff --git a/packages/core/src/api/user-manager.ts b/packages/core/src/api/user-manager.ts index 411b5fd06..cdcc05949 100644 --- a/packages/core/src/api/user-manager.ts +++ b/packages/core/src/api/user-manager.ts @@ -564,18 +564,24 @@ class UserManager { const email = newEmail.toLowerCase(); - await http.patch( - `${constants.AUTH_HOST}${ENDPOINTS.patchUser}`, - { - type: "change_email", - new_email: newEmail, - password: await this.db.storage().hash(password, email, { - usesFallback: await this.usesFallbackPWHash(password) - }), - verification_code: code - }, - token - ); + try { + await http.patch( + `${constants.AUTH_HOST}${ENDPOINTS.patchUser}`, + { + type: "change_email", + new_email: newEmail, + password: await this.db.storage().hash(password, email, { + usesFallback: await this.usesFallbackPWHash(password) + }), + verification_code: code + }, + token + ); + } catch (e) { + const error = e as Error; + if (error.message === "Invalid token.") throw new Error("Invalid code."); + throw error; + } } recoverAccount(email: string) { diff --git a/packages/intl/locale/en.po b/packages/intl/locale/en.po index ee1ec8446..fa6ab07e2 100644 --- a/packages/intl/locale/en.po +++ b/packages/intl/locale/en.po @@ -1,6 +1,7 @@ msgid "" msgstr "" -"POT-Creation-Date: 2026-04-20 11:33+0500\n" +"POT-Creation-Date: 2026-05-11 12:10+0500\n" +"POT-Creation-Date: 2026-05-11 12:10+0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -975,6 +976,10 @@ msgstr "Are you sure you want to clear all logs from {key}?" msgid "Are you sure you want to clear trash?" msgstr "Are you sure you want to clear trash?" +#: src/strings.ts:2734 +msgid "Are you sure you want to delete this attachment?" +msgstr "Are you sure you want to delete this attachment?" + #: src/strings.ts:1544 msgid "Are you sure you want to logout and clear all data stored on THIS DEVICE?" msgstr "Are you sure you want to logout and clear all data stored on THIS DEVICE?" @@ -1032,6 +1037,10 @@ msgstr "attachment" msgid "Attachment" msgstr "Attachment" +#: src/strings.ts:2735 +msgid "Attachment deleted" +msgstr "Attachment deleted" + #: src/strings.ts:2460 msgid "Attachment manager" msgstr "Attachment manager" @@ -1830,6 +1839,10 @@ msgstr "Confirm new password" msgid "Confirm password" msgstr "Confirm password" +#: src/strings.ts:2729 +msgid "Confirm password required" +msgstr "Confirm password required" + #: src/strings.ts:1489 msgid "Confirm pin" msgstr "Confirm pin" @@ -2040,10 +2053,15 @@ msgstr "Curate the toolbar that fits your needs and matches your personality." msgid "Current note" msgstr "Current note" +#: src/strings.ts:1481 #: src/strings.ts:1487 msgid "Current password" msgstr "Current password" +#: src/strings.ts:2738 +msgid "Current password required" +msgstr "Current password required" + #: src/strings.ts:1231 msgid "Current path: {path}" msgstr "Current path: {path}" @@ -2222,6 +2240,10 @@ msgstr "Delete" msgid "Delete account" msgstr "Delete account" +#: src/strings.ts:2732 +msgid "Delete attachment" +msgstr "Delete attachment" + #: src/strings.ts:1321 msgid "Delete collapsed section" msgstr "Delete collapsed section" @@ -3230,6 +3252,10 @@ msgstr "Getting information" msgid "Getting recovery codes" msgstr "Getting recovery codes" +#: src/strings.ts:2731 +msgid "Gift code required" +msgstr "Gift code required" + #: src/strings.ts:2174 msgid "GNU GENERAL PUBLIC LICENSE Version 3" msgstr "GNU GENERAL PUBLIC LICENSE Version 3" @@ -3937,6 +3963,10 @@ msgstr "Login failed" msgid "Login required" msgstr "Login required" +#: src/strings.ts:2739 +msgid "Login required to restore attachments" +msgstr "Login required to restore attachments" + #: src/strings.ts:779 msgid "Login successful" msgstr "Login successful" @@ -4187,6 +4217,10 @@ msgstr "Multi-layer encryption to most important notes" msgid "Name" msgstr "Name" +#: src/strings.ts:2737 +msgid "Name is required." +msgstr "Name is required." + #: src/strings.ts:1690 msgid "Native high-performance encryption" msgstr "Native high-performance encryption" @@ -4568,10 +4602,6 @@ msgstr "Okay" msgid "Old - new" msgstr "Old - new" -#: src/strings.ts:1481 -msgid "Old password" -msgstr "Old password" - #: src/strings.ts:1837 msgid "Older version" msgstr "Older version" @@ -4725,6 +4755,10 @@ msgstr "Password not entered" msgid "Password protection" msgstr "Password protection" +#: src/strings.ts:2728 +msgid "Password required" +msgstr "Password required" + #: src/strings.ts:809 msgid "Password updated" msgstr "Password updated" @@ -4836,6 +4870,7 @@ msgid "Please enter a key name" msgstr "Please enter a key name" #: src/strings.ts:1514 +#: src/strings.ts:2730 msgid "Please enter a valid email address" msgstr "Please enter a valid email address" @@ -4899,6 +4934,10 @@ msgstr "Please fill all the fields to continue." msgid "Please grant notifications permission to add new reminders." msgstr "Please grant notifications permission to add new reminders." +#: src/strings.ts:2745 +msgid "Please login to download attachments." +msgstr "Please login to download attachments." + #: src/strings.ts:873 msgid "Please make sure you have saved the recovery key. Tap one more time to confirm." msgstr "Please make sure you have saved the recovery key. Tap one more time to confirm." @@ -6856,6 +6895,10 @@ msgstr "Title" msgid "Title format" msgstr "Title format" +#: src/strings.ts:2736 +msgid "Title is required" +msgstr "Title is required" + #: src/strings.ts:1190 msgid "To use app lock, you must enable biometrics such as Fingerprint lock or Face ID on your phone." msgstr "To use app lock, you must enable biometrics such as Fingerprint lock or Face ID on your phone." @@ -7681,6 +7724,16 @@ msgstr "You have unsynced notes. Take a backup or sync your notes to avoid losin msgid "You must log out in order to change/reset server URLs." msgstr "You must log out in order to change/reset server URLs." +#: src/strings.ts:2741 +msgid "" +"You need to login to restore attachments from a backup file. [Read more](https://help.notesnook.com/faqs/login-to-restore-attachments-in-backup).\n" +" \n" +"Continue without attachments?" +msgstr "" +"You need to login to restore attachments from a backup file. [Read more](https://help.notesnook.com/faqs/login-to-restore-attachments-in-backup).\n" +" \n" +"Continue without attachments?" + #: src/strings.ts:836 msgid "You simply cannot get any better of a note taking app than @notesnook. The UI is clean and slick, it is feature rich, encrypted, reasonably priced (esp. for students & educators) & open source" msgstr "You simply cannot get any better of a note taking app than @notesnook. The UI is clean and slick, it is feature rich, encrypted, reasonably priced (esp. for students & educators) & open source" diff --git a/packages/intl/locale/pseudo-LOCALE.po b/packages/intl/locale/pseudo-LOCALE.po index 2a0cc525e..c790396bf 100644 --- a/packages/intl/locale/pseudo-LOCALE.po +++ b/packages/intl/locale/pseudo-LOCALE.po @@ -1,6 +1,7 @@ msgid "" msgstr "" -"POT-Creation-Date: 2026-04-20 11:33+0500\n" +"POT-Creation-Date: 2026-05-11 12:10+0500\n" +"POT-Creation-Date: 2026-05-11 12:10+0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -973,6 +974,10 @@ msgstr "" #: src/strings.ts:1326 msgid "Are you sure you want to clear trash?" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2734 +msgid "Are you sure you want to delete this attachment?" msgstr "" #: src/strings.ts:1544 @@ -1030,6 +1035,10 @@ msgstr "" #: src/strings.ts:300 #: src/strings.ts:2356 msgid "Attachment" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2735 +msgid "Attachment deleted" msgstr "" #: src/strings.ts:2460 @@ -1817,6 +1826,10 @@ msgstr "" #: src/strings.ts:1485 msgid "Confirm password" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2729 +msgid "Confirm password required" msgstr "" #: src/strings.ts:1489 @@ -2027,12 +2040,17 @@ msgstr "" #: src/strings.ts:1838 msgid "Current note" -msgstr "" +msgstr "<<<<<<< HEAD" +#: src/strings.ts:1481 #: src/strings.ts:1487 msgid "Current password" msgstr "" +#: src/strings.ts:2738 +msgid "Current password required" +msgstr "" + #: src/strings.ts:1231 msgid "Current path: {path}" msgstr "" @@ -2209,6 +2227,10 @@ msgstr "" #: src/strings.ts:1078 msgid "Delete account" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2732 +msgid "Delete attachment" msgstr "" #: src/strings.ts:1321 @@ -3210,7 +3232,11 @@ msgstr "" #: src/strings.ts:400 msgid "Getting recovery codes" -msgstr "<<<<<<< HEAD" +msgstr "<<<<<<< HEAD<<<<<<< HEAD" + +#: src/strings.ts:2731 +msgid "Gift code required" +msgstr "" #: src/strings.ts:2174 msgid "GNU GENERAL PUBLIC LICENSE Version 3" @@ -3917,6 +3943,10 @@ msgstr "" msgid "Login required" msgstr "" +#: src/strings.ts:2739 +msgid "Login required to restore attachments" +msgstr "" + #: src/strings.ts:779 msgid "Login successful" msgstr "" @@ -4165,6 +4195,10 @@ msgstr "" #: src/strings.ts:887 msgid "Name" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2737 +msgid "Name is required." msgstr "" #: src/strings.ts:1690 @@ -4540,11 +4574,7 @@ msgstr "" #: src/strings.ts:641 msgid "Old - new" -msgstr "" - -#: src/strings.ts:1481 -msgid "Old password" -msgstr "" +msgstr "<<<<<<< HEAD" #: src/strings.ts:1837 msgid "Older version" @@ -4699,6 +4729,10 @@ msgstr "" msgid "Password protection" msgstr "" +#: src/strings.ts:2728 +msgid "Password required" +msgstr "" + #: src/strings.ts:809 msgid "Password updated" msgstr "<<<<<<< HEAD" @@ -4807,9 +4841,10 @@ msgstr "<<<<<<< HEAD" #: src/strings.ts:2674 msgid "Please enter a key name" -msgstr "" +msgstr "<<<<<<< HEAD" #: src/strings.ts:1514 +#: src/strings.ts:2730 msgid "Please enter a valid email address" msgstr "" @@ -4873,6 +4908,10 @@ msgstr "" msgid "Please grant notifications permission to add new reminders." msgstr "" +#: src/strings.ts:2745 +msgid "Please login to download attachments." +msgstr "" + #: src/strings.ts:873 msgid "Please make sure you have saved the recovery key. Tap one more time to confirm." msgstr "" @@ -6813,6 +6852,10 @@ msgstr "" #: src/strings.ts:1159 msgid "Title format" +msgstr "<<<<<<< HEAD" + +#: src/strings.ts:2736 +msgid "Title is required" msgstr "" #: src/strings.ts:1190 @@ -7623,6 +7666,13 @@ msgstr "" msgid "You must log out in order to change/reset server URLs." msgstr "" +#: src/strings.ts:2741 +msgid "" +"You need to login to restore attachments from a backup file. [Read more](https://help.notesnook.com/faqs/login-to-restore-attachments-in-backup).\n" +" \n" +"Continue without attachments?" +msgstr "" + #: src/strings.ts:836 msgid "You simply cannot get any better of a note taking app than @notesnook. The UI is clean and slick, it is feature rich, encrypted, reasonably priced (esp. for students & educators) & open source" msgstr "" @@ -7826,7 +7876,7 @@ msgstr "<<<<<<< HEAD" #: src/strings.ts:2055 msgid "Your support request has been forwarded" -msgstr "" +msgstr "<<<<<<< HEAD" #: src/strings.ts:2064 msgid "Your support request has been forwarded to our support team. We will get back to you via email as soon as possible. If you don't receive an email from us within 24-48 hours, please send us an email directly at support@notesnook.com." diff --git a/packages/intl/src/strings.ts b/packages/intl/src/strings.ts index f4f3ed915..fc5602bb5 100644 --- a/packages/intl/src/strings.ts +++ b/packages/intl/src/strings.ts @@ -1478,7 +1478,7 @@ $day$: Current day (eg. Monday)`, someNotesPublished: () => t`Some notes are published`, unpublishToDelete: () => t`Unpublish notes to delete them`, filterAttachments: () => t`Filter attachments by filename, type or hash`, - oldPassword: () => t`Old password`, + oldPassword: () => t`Current password`, newPassword: () => t`New password`, email: () => t`Email`, emailInvalid: () => t`Invalid email`, @@ -2724,5 +2724,23 @@ Use this if changes from other devices are not appearing on this device. This wi valueMustBeBetween: (min: number, max: number) => t`Value must be between ${min} and ${max}`, pgpPrivateKeyProtected: () => - t`Private key is passphrase-protected. Please provide the decrypted key or a key without a passphrase.` + t`Private key is passphrase-protected. Please provide the decrypted key or a key without a passphrase.`, + passwordRequired: () => t`Password required`, + confirmPasswordRequired: () => t`Confirm password required`, + enterAValidEmailAddress: () => t`Please enter a valid email address`, + giftCodeRequired: () => t`Gift code required`, + deleteAttachment: () => t`Delete attachment`, + deleteAttachmentConfirm: () => + t`Are you sure you want to delete this attachment?`, + attachmentDeleted: () => t`Attachment deleted`, + titleIsRequired: () => t`Title is required`, + nameIsRequired: () => t`Name is required.`, + currentPasswordRequired: () => t`Current password required`, + loginToRestoreAttachments: () => t`Login required to restore attachments`, + loginToRestoreAttachmentsDesc: () => + t`You need to login to restore attachments from a backup file. [Read more](https://help.notesnook.com/faqs/login-to-restore-attachments-in-backup). + +Continue without attachments?`, + pleaseLoginToDownloadAttachments: () => + t`Please login to download attachments.` };