Compare commits

..

8 Commits

Author SHA1 Message Date
Ammar Ahmed
83083134e9 Merge branch 'master' into fix-localization-error
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-12-21 14:09:22 +05:00
01zulfi
61b8960aa5 web: fix mfa fallback string (#7123)
* also add selected style to mfa type selector button

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-20 15:55:42 +05:00
01zulfi
5229408156 web: fix editor toolbar scroll styling (#7113)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-20 12:30:08 +05:00
Ammar Ahmed
8e90adccab mobile: fix localization issues 2024-11-27 14:02:22 +05:00
Abdullah Atta
c19635b16a web: fix direct index access of strings 2024-11-27 13:14:44 +05:00
Abdullah Atta
94e4b00294 intl: fix direct access of strings in object 2024-11-27 13:14:12 +05:00
Abdullah Atta
76908b1fbd intl: generate action for when data type is unknown 2024-11-27 12:27:09 +05:00
Ammar Ahmed
7409c5382d mobile: fix localization error on searching notebook/tags 2024-11-27 10:41:52 +05:00
25 changed files with 1563 additions and 1306 deletions

View File

@@ -460,7 +460,7 @@ export class VaultDialog extends Component {
async _deleteNote() {
try {
await db.vault.remove(this.state.note.id, this.password);
await deleteItems([this.state.note.id], "note");
await deleteItems("note", [this.state.note.id]);
this.close();
} catch (e) {
this._takeErrorAction(e);

View File

@@ -57,10 +57,7 @@ export const SectionHeader = React.memo<
}: SectionHeaderProps) {
const { colors } = useThemeColors();
const { fontScale } = useWindowDimensions();
const groupBy =
strings.groupByStrings[
groupOptions.groupBy as keyof typeof strings.groupByStrings
]?.();
const groupBy = strings.groupByStrings[groupOptions.groupBy]();
const isCompactModeEnabled = useIsCompactModeEnabled(
dataType as "note" | "notebook"
);

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Item, ItemType, VirtualizedGrouping } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef } from "react";
import {
@@ -46,7 +47,6 @@ import { MoveNotebookSheet } from "../sheets/move-notebook";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Heading from "../ui/typography/heading";
import { strings } from "@notesnook/intl";
export const SelectionHeader = React.memo(
({
@@ -103,12 +103,11 @@ export const SelectionHeader = React.memo(
const deleteItem = async () => {
if (!type) return;
presentDialog({
title: strings.doActions.delete[
type as keyof typeof strings.doActions.delete
](selectedItemsList.length),
paragraph: strings.actionConfirmations.delete[
type as keyof typeof strings.doActions.delete
](selectedItemsList.length),
title: strings.doActions.delete.unknown(type, selectedItemsList.length),
paragraph: strings.actionConfirmations.delete.unknown(
type,
selectedItemsList.length
),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: async () => {
@@ -297,15 +296,35 @@ export const SelectionHeader = React.memo(
{
title: strings.moveToTrash(),
onPress: async () => {
deleteItems(
undefined,
useSelectionStore.getState().selectionMode
).then(() => {
useSelectionStore.getState().clearSelection();
useSelectionStore.getState().setSelectionMode(undefined);
});
const selection = useSelectionStore.getState();
if (!selection.selectionMode) return;
await deleteItems(
selection.selectionMode as ItemType,
selection.selectedItemsList
);
selection.clearSelection();
selection.setSelectionMode(undefined);
},
visible: type !== "trash",
visible: type === "note" || type === "notebook",
icon: "delete"
},
{
title: strings.doActions.delete.unknown(
type!,
selectedItemsList.length
),
onPress: async () => {
const selection = useSelectionStore.getState();
if (!selection.selectionMode) return;
await deleteItems(
selection.selectionMode as ItemType,
selection.selectedItemsList
);
selection.clearSelection();
selection.setSelectionMode(undefined);
},
visible:
type !== "trash" && type !== "note" && type !== "notebook",
icon: "delete"
},
{

View File

@@ -301,8 +301,8 @@ export const NotebookSheet = () => {
}}
onPress={async () => {
await deleteItems(
useItemSelectionStore.getState().getSelectedItemIds(),
"notebook"
"notebook",
useItemSelectionStore.getState().getSelectedItemIds()
);
useSelectionStore.getState().clearSelection();
useItemSelectionStore.setState({

View File

@@ -557,7 +557,7 @@ export default function ReminderSheet({
<Button
key={mode}
title={strings.reminderNotificationModes[
mode as keyof typeof strings.reminderNotificationModes
mode as keyof typeof ReminderNotificationModes
]()}
style={{
marginRight: 12,

View File

@@ -254,8 +254,8 @@ export const useActions = ({
item.type === "color"
) {
presentDialog({
title: strings.doActions.delete[item.type](1),
paragraph: strings.actionConfirmations.delete[item.type](1),
title: strings.doActions.delete.unknown(item.type, 1),
paragraph: strings.actionConfirmations.delete.unknown(item.type, 1),
positivePress: async () => {
if (item.type === "reminder") {
await db.reminders.remove(item.id);
@@ -290,7 +290,7 @@ export const useActions = ({
});
} else {
try {
await deleteItems([item.id], item.type);
await deleteItems(item.type, [item.id]);
} catch (e) {
console.error(e);
}
@@ -303,8 +303,8 @@ export const useActions = ({
close();
await sleep(300);
presentDialog({
title: strings.doActions.delete[item.itemType](1),
paragraph: strings.actionConfirmations.delete[item.itemType](1),
title: strings.doActions.delete.unknown(item.itemType, 1),
paragraph: strings.actionConfirmations.delete.unknown(item.itemType, 1),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: async () => {
@@ -313,10 +313,7 @@ export const useActions = ({
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().setSelectionMode(undefined);
ToastManager.show({
heading:
strings.actions.deleted[
item.itemType as keyof typeof strings.actions.deleted
](1),
heading: strings.actions.deleted.unknown(item.itemType, 1),
type: "success",
context: "local"
});
@@ -957,11 +954,10 @@ export const useActions = ({
id: "trash",
title:
item.type !== "notebook" && item.type !== "note"
? strings.doActions.delete[
item.type === "trash"
? item.itemType
: (item.type as keyof typeof strings.doActions.delete)
](1)
? strings.doActions.delete.unknown(
item.type === "trash" ? item.itemType : item.type,
1
)
: strings.moveToTrash(),
icon: "delete-outline",
type: "error",

View File

@@ -39,11 +39,7 @@
"@lingui/react": "4.11.2",
"@lingui/core": "4.11.2",
"react-native-check-version": "^1.3.0",
"react-native-material-menu": "^2.0.0",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"@tanstack/react-query": "^4.36.1"
"react-native-material-menu": "^2.0.0"
},
"sideEffects": false
}

View File

@@ -52,7 +52,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
<SelectionHeader id={route.name} items={notes} type="note" />
<Header
renderedInRoute={route.name}
title={strings.routes[route.name as keyof typeof strings.routes]()}
title={strings.routes[route.name]()}
canGoBack={false}
hasSearch={true}
onSearch={() => {
@@ -72,9 +72,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
dataType="note"
renderedInRoute={route.name}
loading={loading || !isFocused}
headerTitle={strings.routes[
route.name as keyof typeof strings.routes
]?.()}
headerTitle={strings.routes[route.name]()}
placeholder={{
title: route.name?.toLowerCase(),
paragraph: strings.notesEmpty(),

View File

@@ -196,8 +196,7 @@ const NotesPage = ({
<Header
renderedInRoute={route.name}
title={
title ||
strings.routes[route.name as unknown as keyof typeof strings.routes]()
route.name === "Monographs" ? strings.routes[route.name]() : title
}
canGoBack={params?.current?.canGoBack}
hasSearch={true}

View File

@@ -57,7 +57,7 @@ export const HomePicker = createSettingsPicker({
});
},
formatValue: (item) => {
return strings.routes[typeof item === "object" ? item.name : item]();
return strings.routes[typeof item === "object" ? item.name : item]?.();
},
getItemKey: (item) => item.name,
options: MenuItemsList.slice(0, MenuItemsList.length - 1),

View File

@@ -32,7 +32,7 @@ export const STORE_LINK =
export const GROUP = {
default: "default",
None: "none",
none: "none",
abc: "abc",
year: "year",
week: "week",

View File

@@ -17,6 +17,8 @@ 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 { ItemType } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { Linking } from "react-native";
import { db } from "../common/database";
import { presentDialog } from "../components/dialog/functions";
@@ -24,16 +26,18 @@ import { eSendEvent, ToastManager } from "../services/event-manager";
import Navigation from "../services/navigation";
import { useMenuStore } from "../stores/use-menu-store";
import { useRelationStore } from "../stores/use-relation-store";
import { useSelectionStore } from "../stores/use-selection-store";
import { useTagStore } from "../stores/use-tag-store";
import { eOnNotebookUpdated, eUpdateNoteInEditor } from "./events";
import { getParentNotebookId } from "./notebooks";
import { useTagStore } from "../stores/use-tag-store";
import { strings } from "@notesnook/intl";
function confirmDeleteAllNotes(items, type, context) {
return new Promise((resolve) => {
function confirmDeleteAllNotes(
items: string[],
type: "notebook",
context?: string
) {
return new Promise<{ delete: boolean; deleteNotes: boolean }>((resolve) => {
presentDialog({
title: strings.doActions.delete[type](items.length),
title: strings.doActions.delete.notebook(items.length),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: (_inputValue, value) => {
@@ -43,22 +47,21 @@ function confirmDeleteAllNotes(items, type, context) {
},
onClose: () => {
setTimeout(() => {
resolve({ delete: false });
resolve({ delete: false, deleteNotes: false });
});
},
context: context,
check: {
info: `Move all notes in ${
items.length > 1 ? `these ${type}s` : `this ${type}`
} to trash`,
info: strings.deleteContainingNotes(items.length),
type: "transparent"
}
});
});
}
async function deleteNotebook(id, deleteNotes) {
async function deleteNotebook(id: string, deleteNotes: boolean) {
const notebook = await db.notebooks.notebook(id);
if (!notebook) return;
const parentId = getParentNotebookId(id);
if (deleteNotes) {
const noteRelations = await db.relations.from(notebook, "note").get();
@@ -74,14 +77,16 @@ async function deleteNotebook(id, deleteNotes) {
}
}
export const deleteItems = async (items, type, context) => {
const ids = items ? items : useSelectionStore.getState().selectedItemsList;
export const deleteItems = async (
type: ItemType,
itemIds: string[],
context?: string
) => {
if (type === "reminder") {
await db.reminders.remove(...ids);
await db.reminders.remove(...itemIds);
useRelationStore.getState().update();
} else if (type === "note") {
for (const id of ids) {
for (const id of itemIds) {
if (db.monographs.isPublished(id)) {
ToastManager.show({
heading: strings.someNotesPublished(),
@@ -104,20 +109,20 @@ export const deleteItems = async (items, type, context) => {
);
}
} else if (type === "notebook") {
const result = await confirmDeleteAllNotes(ids, "notebook", context);
const result = await confirmDeleteAllNotes(itemIds, "notebook", context);
if (!result.delete) return;
for (const id of ids) {
for (const id of itemIds) {
await deleteNotebook(id, result.deleteNotes);
eSendEvent(eOnNotebookUpdated, await getParentNotebookId(id));
}
} else if (type === "tag") {
presentDialog({
title: strings.doActions.delete.tag(ids.length),
title: strings.doActions.delete.tag(itemIds.length),
positiveText: strings.delete(),
negativeText: strings.cancel(),
paragraph: strings.actionConfirmations.delete.tag(2),
positivePress: async () => {
await db.tags.remove(...ids);
await db.tags.remove(...itemIds);
useTagStore.getState().refresh();
useRelationStore.getState().update();
},
@@ -126,9 +131,9 @@ export const deleteItems = async (items, type, context) => {
return;
}
let deletedIds = [...ids];
const deletedIds = [...itemIds];
if (type === "notebook" || type === "note") {
let message = strings.actions.movedToTrash[type](ids.length);
const message = strings.actions.movedToTrash[type](itemIds.length);
ToastManager.show({
heading: message,
type: "success",
@@ -148,28 +153,21 @@ export const deleteItems = async (items, type, context) => {
});
} else {
ToastManager.show({
heading: strings.deleted(type, ids.length),
heading: strings.actions.deleted.unknown(type, itemIds.length),
type: "success"
});
}
Navigation.queueRoutesForUpdate();
if (!items) {
useSelectionStore.getState().clearSelection();
}
useMenuStore.getState().setColorNotes();
if (type === "notebook") {
ids.forEach(async (id) => {
itemIds.forEach(async (id) => {
eSendEvent(eOnNotebookUpdated, await getParentNotebookId(id));
});
useMenuStore.getState().setMenuPins();
}
};
export const openLinkInBrowser = async (link) => {
try {
Linking.openURL(link);
} catch (error) {
console.log(error.message);
}
export const openLinkInBrowser = async (link: string) => {
Linking.openURL(link);
};

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.0.22",
"version": "3.0.23",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.0.22",
"version": "3.0.23",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -22,6 +22,10 @@
"@notesnook/logger": "file:../../packages/logger",
"@notesnook/theme": "file:../../packages/theme",
"@notesnook/themes-server": "file:../../servers/themes",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5"
@@ -28895,10 +28899,6 @@
"@readme/data-urls": "3.0.0",
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.1-alpha.2",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"absolutify": "^0.1.0",
"buffer": "^6.0.3",
"dayjs": "^1.10.4",

View File

@@ -45,6 +45,10 @@
"@notesnook/themes-server": "file:../../servers/themes",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5"
"react-native": "0.74.5",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"@tanstack/react-query": "^4.36.1"
}
}
}

View File

@@ -369,11 +369,7 @@ function TipTap(props: TipTapProps) {
<Toolbar
editor={editor}
location={"top"}
sx={
isTablet || isMobile
? { overflowX: "scroll", flexWrap: "nowrap" }
: {}
}
sx={isTablet || isMobile ? { flexWrap: "nowrap" } : {}}
tools={toolbarConfig}
defaultFontFamily={fontFamily}
defaultFontSize={fontSize}

View File

@@ -480,9 +480,7 @@ export const AddReminderDialog = DialogManager.register(
checked={p.id === priority}
onChange={() => setPriority(p.id)}
/>
{strings.reminderNotificationModes[
p.title as keyof typeof strings.reminderNotificationModes
]()}
{strings.reminderNotificationModes(p.title)}
</Label>
))}
</Flex>

View File

@@ -258,7 +258,7 @@ function ChooseAuthenticator(props: ChooseAuthenticatorProps) {
justifyContent: "start",
alignItems: "start",
textAlign: "left",
bg: "transparent",
bg: selected === index ? "shade" : "transparent",
px: 0
}}
onClick={() => setSelected(index)}

View File

@@ -130,7 +130,7 @@ export function BillingHistory() {
{transaction.amount} {transaction.currency}
</Text>
<Text as="td" variant="body">
{strings.transactionStatusToText[transaction.status]()}
{strings.transactionStatusToText(transaction.status)}
</Text>
<Text as="td" variant="body">
<Link

View File

@@ -232,10 +232,31 @@ type RecoveryMethod = {
isDangerous?: boolean;
};
const recoveryMethods: RecoveryMethod[] = [
{
type: "key",
testId: "step-recovery-key",
title: () => strings.recoveryKeyMethod(),
description: () => strings.recoveryKeyMethodDesc()
},
{
type: "backup",
testId: "step-backup",
title: () => strings.backupFileMethod(),
description: () => strings.backupFileMethodDesc()
},
{
type: "reset",
testId: "step-reset-account",
title: () => strings.clearDataAndResetMethod(),
description: () => strings.clearDataAndResetMethodDesc(),
isDangerous: true
}
];
function RecoveryMethods(props: BaseRecoveryComponentProps<"methods">) {
const { navigate } = props;
const [selected, setSelected] = useState(0);
const recoveryMethods = strings.accountRecoveryMethods as RecoveryMethod[];
if (isSessionExpired()) {
navigate("new");

View File

@@ -78,15 +78,7 @@ const Tiptap = ({
const isFocusedRef = useRef<boolean>(false);
const [undo, setUndo] = useState(false);
const [redo, setRedo] = useState(false);
const valueRef = useRef({
undo,
redo
});
tabRef.current = tab;
valueRef.current = {
undo,
redo
};
function restoreNoteSelection(state?: NoteState) {
try {
@@ -135,14 +127,8 @@ const Tiptap = ({
editor as Editor,
transaction.getMeta("ignoreEdit")
);
if (valueRef.current.undo !== editor.can().undo()) {
setUndo(editor.can().undo());
}
if (valueRef.current.redo !== editor.can().redo()) {
setRedo(editor.can().redo());
}
},
openAttachmentPicker: (type) => {
globalThis.editorControllers[tab.id]?.openFilePicker(type);
return true;

View File

@@ -34,6 +34,7 @@ import {
useToolbarStore
} from "./stores/toolbar-store.js";
import { ToolbarDefinition } from "./types.js";
import { ScrollContainer } from "@notesnook/ui";
type ToolbarProps = FlexProps & {
editor: Editor;
@@ -89,34 +90,44 @@ export function Toolbar(props: ToolbarProps) {
return (
<>
<Flex
className={["editor-toolbar", className].join(" ")}
sx={{
flexWrap: isMobile ? "nowrap" : "wrap",
overflowX: isMobile ? "auto" : "hidden",
bg: "background",
borderRadius: isMobile ? "0px" : "default",
...sx
}}
{...flexProps}
>
{toolbarTools.map((tools) => {
return (
<ToolbarGroup
key={tools.join("")}
tools={tools}
editor={editor}
groupId={tools.join("")}
sx={{
borderRight: "1px solid var(--separator)",
":last-of-type": { borderRight: "none" },
alignItems: "center"
}}
/>
);
<ScrollContainer
className="tabsScroll"
suppressScrollY
style={{ flex: 1 }}
trackStyle={() => ({
backgroundColor: "transparent",
pointerEvents: "none"
})}
</Flex>
<EditorFloatingMenus editor={editor} />
thumbStyle={() => ({ height: 3 })}
>
<Flex
className={["editor-toolbar", className].join(" ")}
sx={{
flexWrap: isMobile ? "nowrap" : "wrap",
bg: "background",
borderRadius: isMobile ? "0px" : "default",
...sx
}}
{...flexProps}
>
{toolbarTools.map((tools) => {
return (
<ToolbarGroup
key={tools.join("")}
tools={tools}
editor={editor}
groupId={tools.join("")}
sx={{
borderRight: "1px solid var(--separator)",
":last-of-type": { borderRight: "none" },
alignItems: "center"
}}
/>
);
})}
</Flex>
<EditorFloatingMenus editor={editor} />
</ScrollContainer>
</>
);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@ const ACTIONS = [
{
action: "deleted",
label: "deleted",
dataTypes: ["attachment", "reminder", "tag", "note"]
dataTypes: ["attachment", "reminder", "tag", "note", "notebook"]
},
{
action: "movedToTrash",
@@ -206,6 +206,28 @@ const DO_ACTIONS_TEMPLATE = (action, dataTypes) => `${action}: {
${dataTypes}
}`;
const UNKNOWN_DATA_TYPE_TEMPLATE = (
exportName,
action,
types,
fallbackSingularTemplate,
fallbackPluralTemplate
) => `unknown: (type: string, count: number) => {
switch (type) {
${types
.map(
(type) => `case "${type}":
return ${exportName}.${action}.${type}(count);`
)
.join("\n")}
default:
return plural(count, {
one: \`${fallbackSingularTemplate}\`,
other: \`${fallbackPluralTemplate}\`
});
}
}`;
const DATA_TYPES_TEMPLATE = (
type,
singularTemplate,
@@ -219,7 +241,7 @@ const MODULE_TEMPLATE = (exportName, strings) =>
`/* eslint-disable header/header */
// THIS FILE IS GENERATED. DO NOT EDIT MANUALLY.
import { t, plural } from "@lingui/macro";
import { plural } from "@lingui/macro";
export const ${exportName} = {
${strings}
@@ -227,54 +249,80 @@ export const ${exportName} = {
`;
function generateDoActionsStrings() {
return generateStrings(
DO_ACTIONS,
(action, type) => `${action} ${DATA_TYPES[type].singular}`,
(action, type) => `${action} # ${DATA_TYPES[type].plural}`
const exportName = "doActions";
return MODULE_TEMPLATE(
exportName,
generateStrings(
exportName,
DO_ACTIONS,
(action, type) => `${action} ${DATA_TYPES[type].singular}`,
(action, type) => `${action} # ${DATA_TYPES[type].plural}`
)
);
}
function generateActionsStrings() {
return generateStrings(
ACTIONS,
(action, type) => `${DATA_TYPES[type].singularCamelCase} ${action}`,
(action, type) => `# ${DATA_TYPES[type].plural} ${action}`
const exportName = "actions";
return MODULE_TEMPLATE(
exportName,
generateStrings(
exportName,
ACTIONS,
(action, type) => `${DATA_TYPES[type].singularCamelCase} ${action}`,
(action, type) => `# ${DATA_TYPES[type].plural} ${action}`
)
);
}
function generateActionConfirmationStrings() {
return generateStrings(
ACTION_CONFIRMATIONS,
(action, type) =>
`Are you sure you want to ${action} this ${DATA_TYPES[type].singular}?`,
(action, type) =>
`Are you sure you to ${action} these ${DATA_TYPES[type].plural}?`
const exportName = "actionConfirmations";
return MODULE_TEMPLATE(
exportName,
generateStrings(
exportName,
ACTION_CONFIRMATIONS,
(action, type) =>
`Are you sure you want to ${action} this ${DATA_TYPES[type].singular}?`,
(action, type) =>
`Are you sure you to ${action} these ${DATA_TYPES[type].plural}?`
)
);
}
function generateActionErrorStrings() {
return generateStrings(
ACTION_ERRORS,
(action, type) =>
`${DATA_TYPES[type].singularCamelCase} could not be ${action}`,
(action, type) => `# ${DATA_TYPES[type].plural} could not be ${action}`
const exportName = "actionErrors";
return MODULE_TEMPLATE(
exportName,
generateStrings(
exportName,
ACTION_ERRORS,
(action, type) =>
`${DATA_TYPES[type].singularCamelCase} could not be ${action}`,
(action, type) => `# ${DATA_TYPES[type].plural} could not be ${action}`
)
);
}
function generateInProgressActionsStrings() {
return generateStrings(
IN_PROGRESS_ACTIONS,
(action, type) => `${action} ${DATA_TYPES[type].singular}...`,
(action, type) => `${action} # ${DATA_TYPES[type].plural}...`
const exportName = "inProgressActions";
return MODULE_TEMPLATE(
exportName,
generateStrings(
exportName,
IN_PROGRESS_ACTIONS,
(action, type) => `${action} ${DATA_TYPES[type].singular}...`,
(action, type) => `${action} # ${DATA_TYPES[type].plural}...`
)
);
}
function generateStrings(actions, singular, plural) {
function generateStrings(exportName, actions, singular, plural) {
let result = [];
for (const action of actions) {
const subResults = [];
const actionName = action.label;
for (const type of action.dataTypes) {
const actionName = action.label;
subResults.push(
DATA_TYPES_TEMPLATE(
type,
@@ -283,6 +331,15 @@ function generateStrings(actions, singular, plural) {
)
);
}
subResults.push(
UNKNOWN_DATA_TYPE_TEMPLATE(
exportName,
action.action,
action.dataTypes,
singular(actionName, "item"),
plural(actionName, "item")
)
);
result.push(DO_ACTIONS_TEMPLATE(action.action, subResults.join(",\n")));
}
return result.join(",\n");
@@ -290,27 +347,18 @@ function generateStrings(actions, singular, plural) {
mkdirSync("./generated/", { recursive: true });
writeFileSync(
"./generated/do-actions.ts",
MODULE_TEMPLATE("doActions", generateDoActionsStrings())
);
writeFileSync("./generated/do-actions.ts", generateDoActionsStrings());
writeFileSync(
"./generated/actions.ts",
MODULE_TEMPLATE("actions", generateActionsStrings())
);
writeFileSync("./generated/actions.ts", generateActionsStrings());
writeFileSync(
"./generated/in-progress-actions.ts",
MODULE_TEMPLATE("inProgressActions", generateInProgressActionsStrings())
generateInProgressActionsStrings()
);
writeFileSync(
"./generated/action-errors.ts",
MODULE_TEMPLATE("actionErrors", generateActionErrorStrings())
);
writeFileSync("./generated/action-errors.ts", generateActionErrorStrings());
writeFileSync(
"./generated/action-confirmations.ts",
MODULE_TEMPLATE("actionConfirmations", generateActionConfirmationStrings())
generateActionConfirmationStrings()
);

View File

@@ -17,11 +17,11 @@ 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 { plural, select, t } from "@lingui/macro";
import { doActions } from "../generated/do-actions";
import { actions } from "../generated/actions";
import { inProgressActions } from "../generated/in-progress-actions";
import { actionErrors } from "../generated/action-errors";
import { actionConfirmations } from "../generated/action-confirmations";
import { actionErrors } from "../generated/action-errors";
import { actions } from "../generated/actions";
import { doActions } from "../generated/do-actions";
import { inProgressActions } from "../generated/in-progress-actions";
const SEARCH_IN_ROUTE_STRINGS = {
Notes: () => t`Search in in Notes`,
@@ -38,6 +38,19 @@ const SEARCH_IN_ROUTE_STRINGS = {
Monographs: () => t`Search in in Monographs`
};
const TRANSACTION_STATUS = {
completed: () => t`Completed`,
refunded: () => t`"Refunded`,
partially_refunded: () => t`Partially refunded`,
disputed: () => t`Disputed`
};
const REMINDER_NOTIFICATION_MODES = {
Silent: () => t`Silent`,
Vibrate: () => t`Vibrate`,
Urgent: () => t`Urgent`
};
export const strings = {
done: () => t`Done`,
verifyItsYou: () => t`Please verify it's you`,
@@ -607,10 +620,14 @@ $headline$: Use starting line of the note as title.`,
6: () => t`Sat`
},
selectDate: () => t`Select date`,
reminderNotificationModes: {
Silent: () => t`Silent`,
Vibrate: () => t`Vibrate`,
Urgent: () => t`Urgent`
reminderNotificationModes: (
mode: keyof typeof REMINDER_NOTIFICATION_MODES | ({} & string)
) => {
return mode in REMINDER_NOTIFICATION_MODES
? REMINDER_NOTIFICATION_MODES[
mode as keyof typeof REMINDER_NOTIFICATION_MODES
]()
: mode;
},
selectBackupsFolder: () => t`Select backups folder`,
oldNew: () => t`Old - new`,
@@ -630,7 +647,7 @@ $headline$: Use starting line of the note as title.`,
},
groupByStrings: {
default: () => t`Default`,
None: () => t`None`,
none: () => t`None`,
abc: () => t`Abc`,
year: () => t`Year`,
week: () => t`Week`,
@@ -1482,7 +1499,7 @@ For example:
return (
SEARCH_IN_ROUTE_STRINGS[
routeName as keyof typeof SEARCH_IN_ROUTE_STRINGS
]() || t`Search in ${routeName}`
]?.() || t`Search in ${routeName}`
);
},
logoutConfirmation: () =>
@@ -1675,12 +1692,15 @@ For example:
t`Your account is now 100% secure against unauthorized logins.`,
sms: () => t`phone number`,
app: () => t`authentication app`,
mfaFallbackMethodText: (fallback: string, primary: string) =>
`You will now receive your 2FA codes on your ${
strings[fallback as keyof typeof strings]
} in case you lose access to your ${
strings[primary as keyof typeof strings]
}.`,
mfaFallbackMethodText: (
fallback: "app" | "sms" | "email",
primary: "app" | "sms" | "email"
) =>
`You will now receive your 2FA codes on your ${strings[
fallback
]().toLocaleLowerCase()} in case you lose access to your ${strings[
primary
]().toLocaleLowerCase()}.`,
transactionStatusToText: {
completed: () => t`Completed`,
refunded: () => t`"Refunded`,
@@ -1737,32 +1757,18 @@ For example:
notebooksAllCaps: () => t`NOTEBOOKS`,
authenticatedAs: (email?: string) => t`Authenticated as ${email}`,
rememberedYourPassword: () => t`Remembered your password?`,
accountRecoveryMethods: [
{
type: "key",
testId: "step-recovery-key",
title: () => `Use recovery key`,
description: () =>
`Your data recovery key is basically a hashed version of your password (plus some random salt). It can be used to decrypt your data for re-encryption.`
},
{
type: "backup",
testId: "step-backup",
title: () => `Use a backup file`,
description: () =>
`If you don't have a recovery key, you can recover your data by restoring a Notesnook data backup file (.nnbackup).`
},
{
type: "reset",
testId: "step-reset-account",
title: () => `Clear data & reset account`,
description: () =>
`EXTREMELY DANGEROUS! This action is irreversible. All your data including notes, notebooks, attachments & settings will be deleted. This is a full account reset. Proceed with caution.`,
isDangerous: true
}
],
chooseRecoveryMethod: () => t`Choose a recovery method`,
chooseRecoveryMethodDesc: () => t`How do you want to recover your account?`,
recoveryKeyMethod: () => t`Use recovery key`,
recoveryKeyMethodDesc: () =>
t`Your data recovery key is basically a hashed version of your password (plus some random salt). It can be used to decrypt your data for re-encryption.`,
backupFileMethod: () => t`Use a backup file`,
backupFileMethodDesc: () =>
t`If you don't have a recovery key, you can recover your data by restoring a Notesnook data backup file (.nnbackup).`,
clearDataAndResetMethod: () => t`Clear data & reset account`,
clearDataAndResetMethodDesc: () =>
t`EXTREMELY DANGEROUS! This action is irreversible. All your data including notes, notebooks, attachments & settings will be deleted. This is a full account reset. Proceed with caution.`,
browse: () => t`Browse`,
dontShowAgain: () => t`Don't show again`,
dontShowAgainConfirm: () => t`Don't show again on this device?`,