mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f545216dc3 | ||
|
|
989de21e44 | ||
|
|
2c051f9d21 | ||
|
|
4845d40c0b | ||
|
|
b4615fa9aa | ||
|
|
f24ff63c06 | ||
|
|
7518e6badc | ||
|
|
0fcbdbcdc3 | ||
|
|
999856311f | ||
|
|
98f4e08c62 | ||
|
|
f3140cbd8f | ||
|
|
029cadedc4 | ||
|
|
6cdeb48e56 | ||
|
|
2c8abc4d00 | ||
|
|
04820f6500 | ||
|
|
fc6a843a1a | ||
|
|
129dad06c2 | ||
|
|
063b387807 | ||
|
|
e0058fe30b | ||
|
|
a3c297636e | ||
|
|
afc534806d | ||
|
|
4456cc2184 | ||
|
|
2610db90d2 | ||
|
|
f16c9804ee | ||
|
|
7a3a1aa9c2 | ||
|
|
57d0904bee | ||
|
|
5ca6f98c6c | ||
|
|
dbe32e8af6 | ||
|
|
e0758bf353 | ||
|
|
3c1b5d1ee8 | ||
|
|
074008ac67 | ||
|
|
7c56a6beb3 | ||
|
|
3e9d7a70d0 | ||
|
|
e2ca1a0df6 | ||
|
|
392456a0d9 |
120
.github/workflows/android.e2e.yml
vendored
Normal file
120
.github/workflows/android.e2e.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
name: e2e-android
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
e2e-android:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
API_LEVEL: 34
|
||||
ARCH: x86_64
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
tool-cache: false
|
||||
android: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=mobile
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
gradle-version: wrapper
|
||||
cache-read-only: false
|
||||
|
||||
- name: Use specific Java version for the builds
|
||||
uses: joschi/setup-jdk@v2
|
||||
with:
|
||||
java-version: "17"
|
||||
architecture: "x64"
|
||||
|
||||
- name: Install Detox CLI
|
||||
run: npm install detox-cli --global
|
||||
|
||||
- name: Generate debug.keystore
|
||||
working-directory: apps/mobile/native/android/app
|
||||
run: |
|
||||
keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "C=US, O=Android, CN=Android Debug"
|
||||
|
||||
- name: Detox build
|
||||
run: |
|
||||
yarn build:android
|
||||
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
tool-cache: false
|
||||
android: false
|
||||
|
||||
- name: Free Disk Space After Build
|
||||
run: |
|
||||
echo "Disk space before cleanup:"
|
||||
df -h
|
||||
sudo rm -rf /usr/local/lib/android/sdk/ndk
|
||||
sudo apt-get clean
|
||||
echo "Disk space after cleanup:"
|
||||
df -h
|
||||
|
||||
- name: Get device name
|
||||
id: device
|
||||
run: |
|
||||
AVD_NAME=$(node -p "require('./apps/mobile/native/.detoxrc.js').devices.emulator.device.avdName")
|
||||
echo "AVD_NAME=$AVD_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: AVD cache
|
||||
uses: actions/cache@v4
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
key: ${{ steps.device.outputs.AVD_NAME }}
|
||||
|
||||
- name: create AVD and generate snapshot for caching
|
||||
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ env.API_LEVEL }}
|
||||
arch: ${{ env.ARCH }}
|
||||
avd-name: ${{ steps.device.outputs.AVD_NAME }}
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
|
||||
- name: Detox test
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ env.API_LEVEL }}
|
||||
arch: ${{ env.ARCH }}
|
||||
avd-name: ${{ steps.device.outputs.AVD_NAME }}
|
||||
disable-animations: true
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
force-avd-creation: false
|
||||
script: |
|
||||
yarn test:android --headless --record-logs failing --record-videos failing --take-screenshots failing --artifacts-location apps/mobile/native/artifacts
|
||||
|
||||
- name: Upload artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: detox-artifacts
|
||||
path: apps/mobile/native/artifacts
|
||||
retention-days: 14
|
||||
1
apps/mobile/.gitignore
vendored
1
apps/mobile/.gitignore
vendored
@@ -39,6 +39,7 @@ DerivedData
|
||||
*.xcuserstate
|
||||
*.hprof
|
||||
**/.xcode.env.local
|
||||
native/cache
|
||||
# Android/IntelliJ
|
||||
#
|
||||
rn-build-deps/
|
||||
|
||||
@@ -140,7 +140,7 @@ export async function downloadAttachments(attachments) {
|
||||
useAttachmentStore.getState().setDownloading({
|
||||
current: 0,
|
||||
total: 1,
|
||||
message: `${strings.savingZipFile()}... ${strings.pleaseWait()}`,
|
||||
message: strings.savingZipFile(),
|
||||
groupId
|
||||
});
|
||||
// If all goes well, zip the notesnook-attachments folder in cache.
|
||||
@@ -150,9 +150,7 @@ export async function downloadAttachments(attachments) {
|
||||
groupId,
|
||||
current: progress,
|
||||
total: 1,
|
||||
message: `${strings.savingZipFile()} (${(progress * 100).toFixed(
|
||||
1
|
||||
)}%)... ${strings.pleaseWait()}`
|
||||
message: strings.savingZipFile((progress * 100).toFixed(1))
|
||||
});
|
||||
});
|
||||
await zip(zipSourceFolder, zipOutputFile);
|
||||
|
||||
@@ -336,10 +336,8 @@ export const AttachmentDialog = ({
|
||||
onPress() {
|
||||
if (!attachments) return;
|
||||
presentDialog({
|
||||
title: strings.doAction(
|
||||
"attachment",
|
||||
attachments.placeholders.length,
|
||||
"download"
|
||||
title: strings.doActions.download.attachment(
|
||||
attachments.placeholders.length
|
||||
),
|
||||
positiveText: strings.network.download(),
|
||||
positivePress: async () => {
|
||||
@@ -390,10 +388,8 @@ export const AttachmentDialog = ({
|
||||
onPress={() => {
|
||||
if (!attachments) return;
|
||||
presentDialog({
|
||||
title: strings.doAction(
|
||||
"attachment",
|
||||
attachments.placeholders.length,
|
||||
"download"
|
||||
title: strings.doActions.download.attachment(
|
||||
attachments.placeholders.length
|
||||
),
|
||||
positiveText: strings.network.download(),
|
||||
positivePress: async () => {
|
||||
|
||||
@@ -17,8 +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 React, { useEffect, useRef, useState } from "react";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { ScrollView } from "react-native-actions-sheet";
|
||||
import { db } from "../../common/database/index";
|
||||
import useTimer from "../../hooks/use-timer";
|
||||
import {
|
||||
@@ -26,19 +29,14 @@ import {
|
||||
presentSheet,
|
||||
ToastManager
|
||||
} from "../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { eCloseSheet } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Input from "../ui/input";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useCallback } from "react";
|
||||
import { ScrollView } from "react-native-actions-sheet";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
@@ -311,8 +311,10 @@ export class VaultDialog extends Component {
|
||||
loading: true
|
||||
});
|
||||
try {
|
||||
let verified = await db.user.verifyPassword(this.password);
|
||||
if (!(await db.user.getUser())) verified = true;
|
||||
let verified = true;
|
||||
if (await db.user.getUser()) {
|
||||
verified = await db.user.verifyPassword(this.password);
|
||||
}
|
||||
if (verified) {
|
||||
let noteIds = [];
|
||||
if (this.state.deleteAll) {
|
||||
@@ -320,20 +322,21 @@ export class VaultDialog extends Component {
|
||||
const relations = await db.relations.from(vault, "note").get();
|
||||
noteIds = relations.map((item) => item.toId);
|
||||
}
|
||||
|
||||
await db.vault.delete(this.state.deleteAll);
|
||||
|
||||
noteIds.forEach((id) => {
|
||||
eSendEvent(
|
||||
eUpdateNoteInEditor,
|
||||
{
|
||||
id: id,
|
||||
deleted: true
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
if (this.state.deleteAll) {
|
||||
noteIds.forEach((id) => {
|
||||
eSendEvent(
|
||||
eUpdateNoteInEditor,
|
||||
{
|
||||
id: id,
|
||||
deleted: true
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
console.log("VAULT UPDATED EVENT");
|
||||
eSendEvent("vaultUpdated");
|
||||
this.setState({
|
||||
loading: false
|
||||
@@ -517,7 +520,6 @@ export class VaultDialog extends Component {
|
||||
});
|
||||
this.close();
|
||||
} else {
|
||||
eSendEvent("vaultUpdated");
|
||||
ToastManager.show({
|
||||
heading: strings.vaultCreated(),
|
||||
type: "success",
|
||||
@@ -525,6 +527,7 @@ export class VaultDialog extends Component {
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
eSendEvent("vaultUpdated");
|
||||
}
|
||||
|
||||
_permanantUnlock() {
|
||||
|
||||
@@ -33,7 +33,6 @@ import { eScrollEvent } from "../../utils/events";
|
||||
import { LeftMenus } from "./left-menus";
|
||||
import { RightMenus } from "./right-menus";
|
||||
import { Title } from "./title";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
|
||||
type HeaderRightButton = {
|
||||
title: string;
|
||||
|
||||
@@ -17,30 +17,23 @@ 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 { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import {
|
||||
Linking,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View
|
||||
} from "react-native";
|
||||
import { Linking, ScrollView, useWindowDimensions, View } from "react-native";
|
||||
import { SwiperFlatList } from "react-native-swiper-flatlist";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eOpenLoginDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { AuthMode } from "../auth";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { eOpenLoginDialog } from "../../utils/events";
|
||||
import { AuthMode } from "../auth";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
const Intro = ({ navigation }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
@@ -34,12 +34,13 @@ import {
|
||||
Reminder,
|
||||
Tag,
|
||||
TrashItem,
|
||||
VirtualizedGrouping
|
||||
VirtualizedGrouping,
|
||||
getSortValue
|
||||
} from "@notesnook/core";
|
||||
import { getSortValue } from "@notesnook/core";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { eOpenJumpToDialog } from "../../utils/events";
|
||||
@@ -48,7 +49,6 @@ import { NoteWrapper } from "../list-items/note/wrapper";
|
||||
import { NotebookWrapper } from "../list-items/notebook/wrapper";
|
||||
import ReminderItem from "../list-items/reminder";
|
||||
import TagItem from "../list-items/tag";
|
||||
import { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
|
||||
|
||||
type ListItemWrapperProps<TItem = Item> = {
|
||||
group?: GroupingKey;
|
||||
|
||||
@@ -102,9 +102,8 @@ export const SelectionHeader = React.memo(
|
||||
|
||||
const deleteItem = async () => {
|
||||
presentDialog({
|
||||
title: strings.deleteItems(type as string, selectedItemsList.length),
|
||||
paragraph: strings.deleteItemsConfirmation(
|
||||
type as string,
|
||||
title: strings.doActions.delete[type](selectedItemsList.length),
|
||||
paragraph: strings.actionConfirmations.delete[type](
|
||||
selectedItemsList.length
|
||||
),
|
||||
positiveText: strings.delete(),
|
||||
|
||||
@@ -81,7 +81,7 @@ export const AddNotebookSheet = ({
|
||||
id: notebook?.id
|
||||
});
|
||||
|
||||
if (parentNotebook) {
|
||||
if (parentNotebook && id) {
|
||||
await db.relations.add(parentNotebook, {
|
||||
type: "notebook",
|
||||
id: id
|
||||
@@ -94,7 +94,8 @@ export const AddNotebookSheet = ({
|
||||
useNotebookStore.getState().refresh();
|
||||
|
||||
const parent =
|
||||
parentNotebook?.id || (await getParentNotebookId(notebook?.id || id));
|
||||
parentNotebook?.id ||
|
||||
(await getParentNotebookId(notebook?.id || (id as string)));
|
||||
|
||||
eSendEvent(eOnNotebookUpdated, parent);
|
||||
if (notebook) {
|
||||
@@ -104,7 +105,7 @@ export const AddNotebookSheet = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (!notebook && showMoveNotesOnComplete) {
|
||||
if (!notebook && showMoveNotesOnComplete && id) {
|
||||
MoveNotes.present(await db.notebooks.notebook(id));
|
||||
} else {
|
||||
close?.(true);
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Note } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, {
|
||||
RefObject,
|
||||
@@ -49,13 +50,11 @@ import DialogHeader from "../../dialog/dialog-header";
|
||||
import SheetProvider from "../../sheet-provider";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { AddNotebookSheet } from "../add-notebook";
|
||||
import { NotebookItem } from "./notebook-item";
|
||||
import { useNotebookItemSelectionStore } from "./store";
|
||||
import { AddNotebookSheet } from "../add-notebook";
|
||||
import Input from "../../ui/input";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
async function updateInitialSelectionState(items: string[]) {
|
||||
const relations = await db.relations
|
||||
|
||||
@@ -171,7 +171,7 @@ const ExportNotesSheet = ({
|
||||
>
|
||||
<DialogHeader
|
||||
icon="export"
|
||||
title={`${strings.export()} ${strings.notes(ids.length)}`}
|
||||
title={strings.exportNotes(ids.length)}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const NewFeature = ({
|
||||
version
|
||||
}: {
|
||||
features: FeatureType[];
|
||||
version?: string | null;
|
||||
version?: string;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
@@ -57,7 +57,7 @@ const NewFeature = ({
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.secondary.heading} size={SIZE.md}>
|
||||
{!version ? `${strings.newVersion()} ` : `v${version} `}Highlights 🎉
|
||||
{strings.newVersionHighlights(version)}
|
||||
</Heading>
|
||||
|
||||
<Seperator />
|
||||
@@ -122,7 +122,7 @@ NewFeature.present = () => {
|
||||
component: (
|
||||
<NewFeature
|
||||
features={features}
|
||||
version={SettingsService.getProperty("version")}
|
||||
version={SettingsService.getProperty("version") || undefined}
|
||||
/>
|
||||
),
|
||||
disableClosing: true
|
||||
|
||||
@@ -206,7 +206,7 @@ export const NotebookSheet = () => {
|
||||
backgroundInteractionEnabled
|
||||
gestureEnabled
|
||||
>
|
||||
<View
|
||||
{/* <View
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 24 + normalize(50),
|
||||
@@ -214,17 +214,11 @@ export const NotebookSheet = () => {
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
testID={notesnook.buttons.add}
|
||||
testID="add-notebook-button"
|
||||
type="secondary"
|
||||
onPress={() => {
|
||||
if (!notebook) return;
|
||||
AddNotebookSheet.present(
|
||||
undefined,
|
||||
notebook,
|
||||
undefined,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 100
|
||||
@@ -245,7 +239,7 @@ export const NotebookSheet = () => {
|
||||
/>
|
||||
</View>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View> */}
|
||||
|
||||
<View
|
||||
style={{
|
||||
@@ -404,6 +398,27 @@ export const NotebookSheet = () => {
|
||||
height: 40 * fontScale
|
||||
}}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
testID="add-notebook-button"
|
||||
name="notebook-plus"
|
||||
onPress={() => {
|
||||
if (!notebook) return;
|
||||
AddNotebookSheet.present(
|
||||
undefined,
|
||||
notebook,
|
||||
undefined,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
size={22}
|
||||
style={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -121,8 +121,8 @@ const PublishNoteSheet = ({
|
||||
}}
|
||||
>
|
||||
<DialogHeader
|
||||
title={`${strings.publish()} ${strings.note()}`}
|
||||
paragraph={strings.publishDesc()}
|
||||
title={strings.publishNote()}
|
||||
paragraph={strings.publishNoteDesc()}
|
||||
/>
|
||||
|
||||
{publishing ? (
|
||||
|
||||
@@ -16,8 +16,8 @@ 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 { VirtualizedGrouping } from "@notesnook/core";
|
||||
import { Item, ItemReference } from "@notesnook/core";
|
||||
import { Item, ItemReference, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { RefObject, useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
@@ -37,8 +37,6 @@ import SheetProvider from "../../sheet-provider";
|
||||
import { Button } from "../../ui/button";
|
||||
import { PressableProps } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { isStringLiteralOrJsxExpression } from "typescript";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type RelationsListProps = {
|
||||
actionSheetRef: RefObject<ActionSheetRef>;
|
||||
@@ -125,7 +123,9 @@ export const RelationsList = ({
|
||||
// width="100%"
|
||||
type="inverted"
|
||||
icon="plus"
|
||||
title={strings.addItem(referenceType)}
|
||||
title={strings.addItem(
|
||||
referenceType as "notebook" | "tag" | "reminder" | "note"
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
|
||||
@@ -70,7 +70,6 @@ interface InputProps extends TextInputProps {
|
||||
};
|
||||
buttons?: React.ReactNode;
|
||||
onBlurInput?: () => void;
|
||||
onPress?: () => void;
|
||||
height?: number;
|
||||
fontSize?: number;
|
||||
onFocusInput?: () => void;
|
||||
|
||||
@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* eslint-disable no-inner-declarations */
|
||||
import { VAULT_ERRORS } from "@notesnook/core";
|
||||
import {
|
||||
Color,
|
||||
ItemReference,
|
||||
@@ -25,8 +24,11 @@ import {
|
||||
Notebook,
|
||||
Reminder,
|
||||
Tag,
|
||||
TrashItem
|
||||
TrashItem,
|
||||
VAULT_ERRORS,
|
||||
createInternalLink
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DisplayedNotification } from "@notifee/react-native";
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
@@ -39,8 +41,10 @@ import NoteHistory from "../components/note-history";
|
||||
import { AddNotebookSheet } from "../components/sheets/add-notebook";
|
||||
import MoveNoteSheet from "../components/sheets/add-to";
|
||||
import ExportNotesSheet from "../components/sheets/export-notes";
|
||||
import { MoveNotebookSheet } from "../components/sheets/move-notebook";
|
||||
import { MoveNotes } from "../components/sheets/move-notes/movenote";
|
||||
import PublishNoteSheet from "../components/sheets/publish-note";
|
||||
import { ReferencesList } from "../components/sheets/references";
|
||||
import { RelationsList } from "../components/sheets/relations-list/index";
|
||||
import ReminderSheet from "../components/sheets/reminder";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
@@ -60,15 +64,10 @@ import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useTagStore } from "../stores/use-tag-store";
|
||||
import { useUserStore } from "../stores/use-user-store";
|
||||
import Errors from "../utils/errors";
|
||||
import { eOpenLoginDialog, eUpdateNoteInEditor } from "../utils/events";
|
||||
import { deleteItems } from "../utils/functions";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
import { ReferencesList } from "../components/sheets/references";
|
||||
import { createInternalLink } from "@notesnook/core";
|
||||
import { MoveNotebookSheet } from "../components/sheets/move-notebook";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const useActions = ({
|
||||
close,
|
||||
@@ -255,8 +254,8 @@ export const useActions = ({
|
||||
item.type === "color"
|
||||
) {
|
||||
presentDialog({
|
||||
title: strings.deleteItem(item.type),
|
||||
paragraph: strings.deleteItemConfirmation(item.type),
|
||||
title: strings.doActions.delete[item.type](1),
|
||||
paragraph: strings.actionConfirmations.delete[item.type](1),
|
||||
positivePress: async () => {
|
||||
if (item.type === "reminder") {
|
||||
await db.reminders.remove(item.id);
|
||||
@@ -304,8 +303,8 @@ export const useActions = ({
|
||||
close();
|
||||
await sleep(300);
|
||||
presentDialog({
|
||||
title: strings.delete(),
|
||||
paragraph: strings.deleteItemsConfirmation(item.itemType, 1),
|
||||
title: strings.doActions.delete[item.itemType](1),
|
||||
paragraph: strings.actionConfirmations.delete[item.itemType](1),
|
||||
positiveText: strings.delete(),
|
||||
negativeText: strings.cancel(),
|
||||
positivePress: async () => {
|
||||
@@ -314,7 +313,10 @@ export const useActions = ({
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().setSelectionMode(undefined);
|
||||
ToastManager.show({
|
||||
heading: strings.itemDeleted(1, item.itemType),
|
||||
heading:
|
||||
strings.actions.deleted[
|
||||
item.itemType as keyof typeof strings.actions.deleted
|
||||
](1),
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
@@ -955,7 +957,11 @@ export const useActions = ({
|
||||
id: "trash",
|
||||
title:
|
||||
item.type !== "notebook" && item.type !== "note"
|
||||
? strings.doAction(item.type, 1, "delete")
|
||||
? strings.doActions.delete[
|
||||
item.type === "trash"
|
||||
? item.itemType
|
||||
: (item.type as keyof typeof strings.doActions.delete)
|
||||
](1)
|
||||
: strings.moveToTrash(),
|
||||
icon: "delete-outline",
|
||||
type: "error",
|
||||
|
||||
@@ -18,11 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { db } from "../common/database";
|
||||
import BiometricService from "../services/biometrics";
|
||||
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
|
||||
import { db } from "../common/database";
|
||||
|
||||
const VaultStatusCache = {
|
||||
const VaultStatusDefaults = {
|
||||
exists: false,
|
||||
biometryEnrolled: false,
|
||||
isBiometryAvailable: false
|
||||
@@ -35,18 +35,12 @@ export type VaultStatusType = {
|
||||
};
|
||||
|
||||
export const useVaultStatus = () => {
|
||||
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusCache);
|
||||
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusDefaults);
|
||||
|
||||
const checkVaultStatus = useCallback(() => {
|
||||
db.vault?.exists().then(async (exists) => {
|
||||
const available = await BiometricService.isBiometryAvailable();
|
||||
const fingerprint = await BiometricService.hasInternetCredentials();
|
||||
if (
|
||||
VaultStatusCache.exists === exists &&
|
||||
VaultStatusCache.biometryEnrolled === fingerprint &&
|
||||
VaultStatusCache.isBiometryAvailable === available
|
||||
)
|
||||
return;
|
||||
setVaultStatus({
|
||||
exists: exists,
|
||||
biometryEnrolled: fingerprint,
|
||||
|
||||
@@ -76,7 +76,7 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
await results.item(0);
|
||||
setResults(results);
|
||||
if (results.placeholders?.length === 0) {
|
||||
setSearchStatus(`${strings.noResultsFound(query)}`);
|
||||
setSearchStatus(strings.noResultsFound(query));
|
||||
} else {
|
||||
setSearchStatus(undefined);
|
||||
}
|
||||
@@ -125,10 +125,8 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
loading={loading}
|
||||
placeholder={{
|
||||
title: route.name,
|
||||
paragraph:
|
||||
searchStatus ||
|
||||
`${strings.typeAKeywordToSearchIn(route.params?.title)}`,
|
||||
loading: `${strings.searchingFor(currentQuery.current as string)}...`
|
||||
paragraph: searchStatus || strings.searchInRoute(route.params?.title),
|
||||
loading: strings.searchingFor(currentQuery.current as string)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -153,7 +153,7 @@ const restoreBackup = async (options: {
|
||||
if (path === ".nnbackup" || path === "attachments") continue;
|
||||
|
||||
updateProgress({
|
||||
progress: `${strings.restoringBackup()}... (${count++}/${
|
||||
progress: `${strings.restoringBackup()} (${count++}/${
|
||||
extractedBackupFiles.length
|
||||
})`
|
||||
});
|
||||
@@ -242,7 +242,7 @@ const restoreBackup = async (options: {
|
||||
|
||||
await db.transaction(async () => {
|
||||
updateProgress({
|
||||
progress: strings.restoringBackup() + "..."
|
||||
progress: strings.restoringBackup()
|
||||
});
|
||||
await db.backup.import(backup, {
|
||||
encryptionKey,
|
||||
|
||||
@@ -351,9 +351,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
});
|
||||
},
|
||||
description(current) {
|
||||
return `${strings.clearCacheDesc()}. ${strings.currentCacheSize(
|
||||
`${current}`
|
||||
)}`;
|
||||
return strings.clearCacheDesc(current as number);
|
||||
},
|
||||
useHook: () => {
|
||||
const [cacheSize, setCacheSize] = React.useState(0);
|
||||
@@ -444,7 +442,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
}
|
||||
|
||||
updateProgress({
|
||||
progress: `${strings.loggingOut()}... ${strings.pleaseWait()}`
|
||||
progress: strings.loggingOut()
|
||||
});
|
||||
|
||||
await db.user?.logout();
|
||||
|
||||
@@ -17,8 +17,10 @@ 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 { strings } from "@notesnook/intl";
|
||||
import React from "react";
|
||||
import { Linking, Platform, View } from "react-native";
|
||||
import Config from "react-native-config";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { usePricing } from "../../hooks/use-pricing";
|
||||
import {
|
||||
@@ -28,14 +30,9 @@ import {
|
||||
} from "../../services/event-manager";
|
||||
import PremiumService from "../../services/premium";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import {
|
||||
SUBSCRIPTION_PROVIDER,
|
||||
SUBSCRIPTION_STATUS
|
||||
} from "../../utils/constants";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { eOpenPremiumDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import Config from "react-native-config";
|
||||
import { strings } from "@notesnook/intl";
|
||||
export const Subscription = () => {
|
||||
const user = useUserStore((state) => state.user);
|
||||
const monthlyPlan = usePricing("monthly");
|
||||
|
||||
@@ -33,7 +33,7 @@ import { strings } from "@notesnook/intl";
|
||||
function confirmDeleteAllNotes(items, type, context) {
|
||||
return new Promise((resolve) => {
|
||||
presentDialog({
|
||||
title: strings.doAction("type", items.length, "delete"),
|
||||
title: strings.doActions.delete[type](items.length),
|
||||
positiveText: strings.delete(),
|
||||
negativeText: strings.cancel(),
|
||||
positivePress: (_inputValue, value) => {
|
||||
@@ -112,10 +112,10 @@ export const deleteItems = async (items, type, context) => {
|
||||
}
|
||||
} else if (type === "tag") {
|
||||
presentDialog({
|
||||
title: strings.deleteTags(ids.length),
|
||||
title: strings.doActions.delete.tag(ids.length),
|
||||
positiveText: strings.delete(),
|
||||
negativeText: strings.cancel(),
|
||||
paragraph: strings.deleteTagsConfirm(),
|
||||
paragraph: strings.actionConfirmations.delete.tag(2),
|
||||
positivePress: async () => {
|
||||
await db.tags.remove(...ids);
|
||||
useTagStore.getState().refresh();
|
||||
@@ -128,7 +128,7 @@ export const deleteItems = async (items, type, context) => {
|
||||
|
||||
let deletedIds = [...ids];
|
||||
if (type === "notebook" || type === "note") {
|
||||
let message = strings.movedToTrash(type, ids.length);
|
||||
let message = strings.actions.movedToTrash[type](ids.length);
|
||||
ToastManager.show({
|
||||
heading: message,
|
||||
type: "success",
|
||||
|
||||
29
apps/mobile/e2e/globalSetup.ts
Normal file
29
apps/mobile/e2e/globalSetup.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { execSync } from "child_process";
|
||||
|
||||
import { pathExists, ensureDir } from "fs-extra";
|
||||
|
||||
import { resolveConfig } from "detox/internals";
|
||||
import { globalSetup } from "detox/runners/jest";
|
||||
|
||||
export default async function customGlobalSetup() {
|
||||
const config = await resolveConfig();
|
||||
if (config.device.type === "android.emulator") {
|
||||
await downloadTestButlerAPK();
|
||||
}
|
||||
|
||||
await globalSetup();
|
||||
}
|
||||
|
||||
async function downloadTestButlerAPK() {
|
||||
const version = "2.2.1";
|
||||
const artifactUrl = `https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/${version}/test-butler-app-${version}.apk`;
|
||||
const filePath = `cache/test-butler-app.apk`;
|
||||
|
||||
await ensureDir("cache");
|
||||
if (!(await pathExists(filePath))) {
|
||||
console.log(`\nDownloading Test-Butler APK v${version}...`);
|
||||
execSync(`curl -f -o ${filePath} ${artifactUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = customGlobalSetup;
|
||||
@@ -1,20 +1,21 @@
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */
|
||||
module.exports = {
|
||||
rootDir: "..",
|
||||
testMatch: ["<rootDir>/e2e/**/*.e2e.js"],
|
||||
testTimeout: 120000,
|
||||
maxWorkers: 1,
|
||||
setupFilesAfterEnv: ["./e2e/setup.js"],
|
||||
globalSetup: "detox/runners/jest/globalSetup",
|
||||
testTimeout: 120000,
|
||||
globalSetup: "./e2e/globalSetup.ts",
|
||||
globalTeardown: "detox/runners/jest/globalTeardown",
|
||||
reporters: ["detox/runners/jest/reporter"],
|
||||
setupFilesAfterEnv: ["./e2e/setup.ts"],
|
||||
testEnvironment: "detox/runners/jest/testEnvironment",
|
||||
verbose: true,
|
||||
reporters: ["detox/runners/jest/reporter"],
|
||||
testRunner: "jest-circus/runner",
|
||||
testMatch: ["<rootDir>/e2e/**/*.e2e.(js|ts)"],
|
||||
transform: {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": [
|
||||
"\\.tsx?$": "ts-jest",
|
||||
"^.+\\.(js|jsx)$": [
|
||||
"babel-jest",
|
||||
{ configFile: "../native/babel.config.js" }
|
||||
]
|
||||
},
|
||||
transformIgnorePatterns: ["<rootDir>/../node_modules/"]
|
||||
verbose: true
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ async function login() {
|
||||
// }
|
||||
|
||||
describe("AUTH", () => {
|
||||
it("Login", async () => {
|
||||
it.skip("Login", async () => {
|
||||
await prepare();
|
||||
await openSideMenu();
|
||||
await login();
|
||||
|
||||
@@ -17,23 +17,22 @@ 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 { navigate, tapByText, prepare, sleep } from "./utils";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
describe("APP LAUNCH AND NAVIGATION", () => {
|
||||
it("App should launch successfully & hide welcome screen", async () => {
|
||||
await prepare();
|
||||
await Tests.prepare();
|
||||
});
|
||||
|
||||
it("Basic navigation should work", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await navigate("Favorites");
|
||||
await navigate("Trash");
|
||||
await navigate("Tags");
|
||||
await navigate("Settings");
|
||||
await navigate("Monographs");
|
||||
await navigate("Reminders");
|
||||
await navigate("Notes");
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.navigate("Favorites");
|
||||
await Tests.navigate("Trash");
|
||||
await Tests.navigate("Tags");
|
||||
await Tests.navigate("Settings");
|
||||
await Tests.navigate("Monographs");
|
||||
await Tests.navigate("Reminders");
|
||||
await Tests.navigate("Notes");
|
||||
});
|
||||
});
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
navigate,
|
||||
tapById,
|
||||
visibleByText,
|
||||
createNote,
|
||||
prepare,
|
||||
visibleById,
|
||||
notVisibleById,
|
||||
sleep,
|
||||
exitEditor,
|
||||
tapByText,
|
||||
elementByText,
|
||||
elementById
|
||||
} from "./utils";
|
||||
|
||||
describe("NOTE TESTS", () => {
|
||||
it("Create a note in editor", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
});
|
||||
|
||||
it("Open and close a note", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.ids.note.get(0));
|
||||
await exitEditor();
|
||||
});
|
||||
|
||||
it.only("Notes properties should show", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await waitFor(elementByText("Created at:")).toBeVisible().withTimeout(500);
|
||||
});
|
||||
|
||||
it("Favorite and unfavorite a note", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-favorite");
|
||||
await visibleById("icon-star");
|
||||
await navigate("Favorites");
|
||||
await visibleByText(note.body);
|
||||
await sleep(500);
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-favorite");
|
||||
await expect(element(by.text(note.body))).not.toBeVisible();
|
||||
await navigate("Notes");
|
||||
});
|
||||
|
||||
it("Pin a note to top", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-pin");
|
||||
await visibleByText("Pinned");
|
||||
await visibleById("icon-pinned");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-pin");
|
||||
expect(element(by.id("icon-pinned"))).not.toBeVisible();
|
||||
});
|
||||
|
||||
it("Pin a note in notifications", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-pin-to-notifications");
|
||||
await visibleByText("Unpin from notifications");
|
||||
await sleep(500);
|
||||
await tapById("icon-pin-to-notifications");
|
||||
await sleep(500);
|
||||
await visibleByText("Pin to notifications");
|
||||
});
|
||||
|
||||
it("Copy note", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await sleep(1000);
|
||||
await waitFor(elementById("icon-copy")).toBeVisible().withTimeout(500);
|
||||
await tapById("icon-copy");
|
||||
});
|
||||
|
||||
it("Assign colors to a note", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await sleep(1000);
|
||||
await tapByText("Add color");
|
||||
await sleep(500);
|
||||
await elementById("color-title-input").typeText("Test color");
|
||||
await tapByText("Add color");
|
||||
await sleep(3000);
|
||||
await visibleById("icon-check");
|
||||
await tapById("icon-color-#efefef");
|
||||
await notVisibleById("icon-check");
|
||||
await tapById("icon-color-#efefef");
|
||||
await device.pressBack();
|
||||
await navigate("Test color");
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
|
||||
it("Delete & restore a note", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await sleep(500);
|
||||
await tapById("icon-trash");
|
||||
await navigate("Trash");
|
||||
await sleep(500);
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await sleep(500);
|
||||
await tapByText("Restore note");
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText(
|
||||
"Test note description that is very long and should not fit in text."
|
||||
);
|
||||
});
|
||||
});
|
||||
116
apps/mobile/e2e/tests/note.e2e.ts
Normal file
116
apps/mobile/e2e/tests/note.e2e.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
describe("NOTE TESTS", () => {
|
||||
it("Create a note in editor", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
});
|
||||
|
||||
it("Open and close a note", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
|
||||
await Tests.exitEditor();
|
||||
});
|
||||
|
||||
it("Notes properties should show", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromText("Created at").isVisible();
|
||||
});
|
||||
|
||||
it("Favorite and unfavorite a note", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-favorite").waitAndTap();
|
||||
await Tests.fromId("icon-star").isVisible();
|
||||
await Tests.navigate("Favorites");
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-favorite").waitAndTap();
|
||||
await Tests.fromText(note.body).isNotVisible();
|
||||
await Tests.navigate("Notes");
|
||||
});
|
||||
|
||||
it("Pin a note to top", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-pin").waitAndTap();
|
||||
await Tests.fromText("Pinned").isVisible();
|
||||
await Tests.fromId("icon-pinned").isVisible();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-pin").waitAndTap();
|
||||
await Tests.fromText("icon-pinned").isNotVisible();
|
||||
});
|
||||
|
||||
it("Pin a note in notifications", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
|
||||
await Tests.fromText("Unpin from notifications").isVisible();
|
||||
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
|
||||
await Tests.fromText("Pin to notifications").isVisible();
|
||||
});
|
||||
|
||||
it("Copy note", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-copy").isVisible();
|
||||
await Tests.fromId("icon-copy").waitAndTap();
|
||||
});
|
||||
|
||||
it("Assign colors to a note", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromText("Add color").waitAndTap();
|
||||
await Tests.fromId("color-title-input").element.typeText("Test color");
|
||||
await Tests.fromText("Add color").waitAndTap();
|
||||
await Tests.fromId("icon-check").isVisible();
|
||||
await Tests.fromId("icon-color-#efefef").waitAndTap();
|
||||
await Tests.fromId("icon-check").isNotVisible();
|
||||
await Tests.fromId("icon-color-#efefef").waitAndTap();
|
||||
await device.pressBack();
|
||||
await Tests.navigate("Test color");
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
});
|
||||
|
||||
it.only("Delete & restore a note", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromId("icon-trash").waitAndTap();
|
||||
await Tests.navigate("Trash");
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.fromText("Restore note").waitAndTap();
|
||||
await device.pressBack();
|
||||
await Tests.fromText(
|
||||
"Test note description that is very long and should not fit in text."
|
||||
).isVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
tapById,
|
||||
elementById,
|
||||
visibleByText,
|
||||
tapByText,
|
||||
createNote,
|
||||
prepare,
|
||||
notVisibleById,
|
||||
navigate,
|
||||
elementByText,
|
||||
sleep,
|
||||
notVisibleByText,
|
||||
visibleById,
|
||||
createNotebook
|
||||
} from "./utils";
|
||||
|
||||
// async function addTopic(title = "Topic") {
|
||||
// await tapById(notesnook.buttons.add);
|
||||
// await elementById("input-title").typeText(title);
|
||||
// await tapByText("Add");
|
||||
// await sleep(500);
|
||||
// }
|
||||
|
||||
describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook with title only", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
});
|
||||
|
||||
it("Create a notebook title & description", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
});
|
||||
|
||||
it("Create a notebook, move notes", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await tapById("listitem.select");
|
||||
await tapByText("Move selected notes");
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(500);
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
|
||||
it("Add a sub notebook to a notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(500);
|
||||
await tapById("add-notebook-button");
|
||||
await createNotebook("Sub notebook", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await visibleByText("Sub notebook");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Move to trash");
|
||||
await sleep(300);
|
||||
await tapByText("Delete");
|
||||
await notVisibleByText("Sub notebook");
|
||||
});
|
||||
|
||||
it("Remove a sub notebook from notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(500);
|
||||
await tapById("add-notebook-button");
|
||||
await createNotebook("Sub notebook", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await visibleByText("Sub notebook");
|
||||
});
|
||||
|
||||
it("Edit notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapById(notesnook.buttons.add);
|
||||
await createNotebook("Notebook 1", true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Edit notebook");
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
" (edited)"
|
||||
);
|
||||
await tapByText("Save");
|
||||
await visibleByText("Notebook 1 (edited)");
|
||||
});
|
||||
|
||||
it("Edit a sub notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapById(notesnook.buttons.add);
|
||||
await createNotebook("Notebook 1", true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(500);
|
||||
await tapById("add-notebook-button");
|
||||
await createNotebook("Sub notebook", true, true);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Edit notebook");
|
||||
await sleep(500);
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
" (edited)"
|
||||
);
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
await visibleByText("Sub notebook (edited)");
|
||||
});
|
||||
|
||||
it("Add a note to notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Add your first notebook");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await createNote();
|
||||
});
|
||||
|
||||
it("Remove note from Notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await sleep(500);
|
||||
let note = await createNote();
|
||||
await elementByText(note.body).longPress();
|
||||
await tapById("select-minus");
|
||||
await notVisibleById(note.title);
|
||||
});
|
||||
|
||||
it("Add/Remove note to notebook from home", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await navigate("Notes");
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await tapByText("Save");
|
||||
await sleep(300);
|
||||
await visibleByText("Notebook 1");
|
||||
});
|
||||
|
||||
it("Edit notebook title, description", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook();
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Edit notebook");
|
||||
await sleep(500);
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
" (Edited)"
|
||||
);
|
||||
await elementById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).clearText();
|
||||
await elementById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).typeText("Description of Notebook 1 (Edited)");
|
||||
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1 (Edited)");
|
||||
await visibleByText("Description of Notebook 1 (Edited)");
|
||||
});
|
||||
|
||||
it.skip("Move notebook to trash", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Move to trash");
|
||||
await sleep(2000);
|
||||
await tapByText("Delete");
|
||||
await sleep(4000);
|
||||
await navigate("Trash");
|
||||
await visibleByText("Notebook 1");
|
||||
});
|
||||
|
||||
it("Move notebook to trash with notes", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", false, true);
|
||||
await sleep(500);
|
||||
await tapById("listitem.select");
|
||||
await tapByText("Move selected notes");
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Move to trash");
|
||||
await sleep(2000);
|
||||
await tapByText("Move all notes in this notebook to trash");
|
||||
await sleep(500);
|
||||
await tapByText("Delete");
|
||||
await sleep(4000);
|
||||
await navigate("Trash");
|
||||
await visibleByText("Notebook 1");
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
|
||||
it("Pin notebook to side menu", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await sleep(500);
|
||||
await tapByText("Add your first notebook");
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await visibleByText("Notebook 1");
|
||||
await tapById(notesnook.ids.notebook.menu);
|
||||
await tapByText("Add Shortcut");
|
||||
let menu = elementById(notesnook.ids.default.header.buttons.left);
|
||||
await menu.tap();
|
||||
await visibleByText("Notebook 1");
|
||||
});
|
||||
});
|
||||
233
apps/mobile/e2e/tests/notebook.e2e.ts
Normal file
233
apps/mobile/e2e/tests/notebook.e2e.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook with title only", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
|
||||
it("Create a notebook title & description", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
|
||||
it("Create a notebook, move notes", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await Tests.fromId("listitem.select").waitAndTap();
|
||||
await Tests.fromText("Move selected notes").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
});
|
||||
|
||||
it("Add a sub notebook to a notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("add-notebook-button").waitAndTap();
|
||||
await Tests.createNotebook("Sub notebook", true);
|
||||
await Tests.fromText("Sub notebook").isVisible();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.fromText("Sub notebook").isNotVisible();
|
||||
});
|
||||
|
||||
it("Remove a sub notebook from notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("add-notebook-button").waitAndTap();
|
||||
await Tests.createNotebook("Sub notebook", true);
|
||||
await Tests.fromText("Sub notebook").isVisible();
|
||||
});
|
||||
|
||||
it("Edit notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromId(notesnook.buttons.add).waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Notebook 1 (edited)").isVisible();
|
||||
});
|
||||
|
||||
it("Edit a sub notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromId(notesnook.buttons.add).waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("add-notebook-button").waitAndTap();
|
||||
await Tests.createNotebook("Sub notebook", true);
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Sub notebook (edited)").isVisible();
|
||||
});
|
||||
|
||||
it("Add a note to notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.createNote();
|
||||
});
|
||||
|
||||
it.skip("Remove note from Notebook", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromText(note.body).element.longPress();
|
||||
await Tests.fromId("select-minus").waitAndTap();
|
||||
await Tests.fromId(note.title).isNotVisible();
|
||||
});
|
||||
|
||||
it("Add/Remove note to notebook from home", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", true);
|
||||
await device.pressBack();
|
||||
await Tests.navigate("Notes");
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("icon-notebooks").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").waitAndTap();
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
|
||||
it("Edit notebook title, description", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook();
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Edit notebook").waitAndTap();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
).element.typeText(" (Edited)");
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).element.clearText();
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).element.typeText("Description of Notebook 1 (Edited)");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("Notebook 1 (Edited)").isVisible();
|
||||
await Tests.fromText("Description of Notebook 1 (Edited)").isVisible();
|
||||
});
|
||||
|
||||
it.skip("Move notebook to trash", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.navigate("Trash");
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
|
||||
it("Move notebook to trash with notes", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await Tests.fromId("listitem.select").waitAndTap();
|
||||
await Tests.fromText("Move selected notes").waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Move to trash").waitAndTap();
|
||||
await Tests.fromText(
|
||||
"Move all notes in this notebook to trash"
|
||||
).waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.navigate("Trash");
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
});
|
||||
|
||||
it.skip("Pin notebook to side menu", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.navigate("Notebooks");
|
||||
await Tests.fromText("Add your first notebook").waitAndTap();
|
||||
await Tests.createNotebook("Notebook 1", false);
|
||||
await device.pressBack();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add Shortcut").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
|
||||
await Tests.fromText("Notebook 1").isVisible();
|
||||
});
|
||||
});
|
||||
@@ -17,29 +17,17 @@ 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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
tapById,
|
||||
visibleByText,
|
||||
createNote,
|
||||
prepare,
|
||||
elementById,
|
||||
sleep,
|
||||
tapByText
|
||||
} from "./utils";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
describe("Search", () => {
|
||||
it("Search for a note", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById("icon-search");
|
||||
await sleep(300);
|
||||
await elementById("search-input").typeText("Test");
|
||||
await sleep(1000);
|
||||
await tapByText(note.body);
|
||||
await sleep(1000);
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId("icon-search").waitAndTap();
|
||||
await Tests.fromId("search-input").element.typeText("Test");
|
||||
await Tests.fromText(note.body).waitAndTap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await visibleByText(note.body);
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
tapById,
|
||||
visibleByText,
|
||||
createNote,
|
||||
prepare,
|
||||
tapByText,
|
||||
notVisibleByText,
|
||||
sleep,
|
||||
navigate,
|
||||
createNotebook
|
||||
} from "./utils";
|
||||
|
||||
async function sortBy(sorting, elementText = "Default") {
|
||||
await tapByText(elementText);
|
||||
await tapByText(sorting);
|
||||
await device.pressBack();
|
||||
}
|
||||
|
||||
describe("Sort & filter", () => {
|
||||
it("Sort by date-edited/date-created", async () => {
|
||||
await prepare();
|
||||
let webview = web(by.id(notesnook.editor.id));
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await createNote("Note 2", "Note 2");
|
||||
await sleep(300);
|
||||
await tapByText("Note 1");
|
||||
await sleep(500);
|
||||
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
|
||||
await webview.element(by.web.className("ProseMirror")).tap();
|
||||
await webview
|
||||
.element(by.web.className("ProseMirror"))
|
||||
.typeText("Edited ", true);
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await sortBy("Date created");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
//await visibleByText("Note 2");
|
||||
await device.pressBack();
|
||||
await sortBy("Date edited");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
//await visibleByText("Edited Note 1");
|
||||
await device.pressBack();
|
||||
});
|
||||
|
||||
it("Disable grouping", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await sortBy("None");
|
||||
await sleep(300);
|
||||
await visibleByText("None");
|
||||
});
|
||||
|
||||
it("Group by Abc", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await sortBy("Abc");
|
||||
await visibleByText("N");
|
||||
});
|
||||
|
||||
it("Group by Year", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await sortBy("Year");
|
||||
await sleep(300);
|
||||
await visibleByText("Year");
|
||||
});
|
||||
|
||||
it("Group by Week", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await sortBy("Week");
|
||||
await sleep(300);
|
||||
await visibleByText("Week");
|
||||
});
|
||||
|
||||
it("Group by Month", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await sortBy("Month");
|
||||
await sleep(300);
|
||||
await visibleByText("Month");
|
||||
});
|
||||
|
||||
it("Compact mode", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
await sleep(300);
|
||||
await tapById("icon-compact-mode");
|
||||
await sleep(300);
|
||||
await notVisibleByText("Note 1");
|
||||
await tapById("icon-compact-mode");
|
||||
await sleep(300);
|
||||
await visibleByText("Note 1");
|
||||
});
|
||||
});
|
||||
93
apps/mobile/e2e/tests/sort.e2e.ts
Normal file
93
apps/mobile/e2e/tests/sort.e2e.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
async function sortBy(sorting: string, elementText = "Default") {
|
||||
await Tests.fromText(elementText).waitAndTap();
|
||||
await Tests.fromText(sorting).waitAndTap();
|
||||
await device.pressBack();
|
||||
}
|
||||
|
||||
describe("Sort & filter", () => {
|
||||
it("Sort by date-edited/date-created", async () => {
|
||||
await Tests.prepare();
|
||||
let webview = web();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await Tests.createNote("Note 2", "Note 2");
|
||||
await Tests.fromText("Note 1").waitAndTap();
|
||||
await webview.element(by.web.className("ProseMirror")).tap();
|
||||
await webview
|
||||
.element(by.web.className("ProseMirror"))
|
||||
.typeText("Edited ", true);
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await sortBy("Date created");
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await device.pressBack();
|
||||
await sortBy("Date edited");
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await device.pressBack();
|
||||
});
|
||||
|
||||
it("Disable grouping", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("None");
|
||||
await Tests.fromText("Default").isNotVisible();
|
||||
});
|
||||
|
||||
it("Group by Abc", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Abc");
|
||||
await Tests.fromText("N").isVisible();
|
||||
});
|
||||
|
||||
it("Group by Year", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Year");
|
||||
await Tests.fromText("Year").isVisible();
|
||||
});
|
||||
|
||||
it("Group by Week", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Week");
|
||||
await Tests.fromText("Week").isVisible();
|
||||
});
|
||||
|
||||
it("Group by Month", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await sortBy("Month");
|
||||
await Tests.fromText("Month").isVisible();
|
||||
});
|
||||
|
||||
it("Compact mode", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote("Note 1", "Note 1");
|
||||
await Tests.fromId("icon-compact-mode").waitAndTap();
|
||||
await Tests.fromText("Note 1").isNotVisible();
|
||||
await Tests.fromId("icon-compact-mode").waitAndTap();
|
||||
await Tests.fromText("Note 1").isVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
navigate,
|
||||
tapById,
|
||||
visibleByText,
|
||||
createNote,
|
||||
prepare,
|
||||
elementById,
|
||||
tapByText,
|
||||
notVisibleByText,
|
||||
sleep
|
||||
} from "./utils";
|
||||
|
||||
describe("Tags", () => {
|
||||
it("Tag a note", async () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Add tags");
|
||||
await elementById("tag-input").typeText("testtag");
|
||||
await tapByText('Add "#testtag"');
|
||||
await visibleByText("#testtag");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await navigate("Tags");
|
||||
await tapByText("#testtag");
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
|
||||
it("Untag a note", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Add tags");
|
||||
await elementById("tag-input").typeText("testtag");
|
||||
await tapByText('Add "#testtag"');
|
||||
await visibleByText("#testtag");
|
||||
await tapByText("#testtag");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await notVisibleByText("#testtag");
|
||||
});
|
||||
|
||||
it("Creat shortcut of a tag", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Add tags");
|
||||
await elementById("tag-input").typeText("testtag");
|
||||
await tapByText('Add "#testtag"');
|
||||
await visibleByText("#testtag");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await navigate("Tags");
|
||||
await sleep(500);
|
||||
await tapById(notesnook.ids.tag.menu);
|
||||
await sleep(500);
|
||||
await tapByText("Add Shortcut");
|
||||
let menu = elementById(notesnook.ids.default.header.buttons.left);
|
||||
await menu.tap();
|
||||
await visibleByText("#testtag");
|
||||
});
|
||||
|
||||
it("Rename a tag", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Add tags");
|
||||
await elementById("tag-input").typeText("testtag");
|
||||
await tapByText('Add "#testtag"');
|
||||
await visibleByText("#testtag");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await navigate("Tags");
|
||||
await tapById(notesnook.ids.tag.menu);
|
||||
await sleep(500);
|
||||
await tapByText("Rename tag");
|
||||
await sleep(500);
|
||||
await elementById("input-value").clearText();
|
||||
await elementById("input-value").typeText("testtag_edited");
|
||||
await tapByText("Save");
|
||||
await visibleByText("#testtag_edited");
|
||||
});
|
||||
});
|
||||
95
apps/mobile/e2e/tests/tag.e2e.ts
Normal file
95
apps/mobile/e2e/tests/tag.e2e.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
describe("Tags", () => {
|
||||
it("Tag a note", async () => {
|
||||
await Tests.prepare();
|
||||
let note = await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.navigate("Tags");
|
||||
await Tests.fromText("#testtag").waitAndTap();
|
||||
await Tests.fromText(note.body).isVisible();
|
||||
});
|
||||
|
||||
it("Untag a note", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await Tests.fromText("#testtag").waitAndTap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromText("#testtag").isNotVisible();
|
||||
});
|
||||
|
||||
it("Create shortcut of a tag", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.navigate("Tags");
|
||||
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add shortcut").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
|
||||
await Tests.fromText("testtag").isVisible();
|
||||
});
|
||||
|
||||
it("Rename a tag", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Add tag").waitAndTap();
|
||||
await Tests.fromId("tag-input").element.typeText("testtag");
|
||||
await Tests.fromText('Add "#testtag"').waitAndTap();
|
||||
await Tests.fromText("#testtag").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.navigate("Tags");
|
||||
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Rename").waitAndTap();
|
||||
await Tests.sleep(100);
|
||||
await Tests.fromId("input-value").element.clearText();
|
||||
await Tests.fromId("input-value").element.typeText("testtag_edited");
|
||||
await Tests.fromText("Save").waitAndTap();
|
||||
await Tests.fromText("#testtag_edited").isVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import { readFileSync } from "fs";
|
||||
import { expect as jestExpect } from "@jest/globals";
|
||||
import { toMatchImageSnapshot } from "jest-image-snapshot";
|
||||
import { device as _device } from "detox";
|
||||
jestExpect.extend({ toMatchImageSnapshot });
|
||||
|
||||
const sleep = (duration) =>
|
||||
new Promise((resolve) =>
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, duration)
|
||||
);
|
||||
|
||||
async function LaunchApp() {
|
||||
await waitFor(element(by.id(notesnook.ids.default.root)))
|
||||
.toBeVisible()
|
||||
.withTimeout(500);
|
||||
}
|
||||
|
||||
function elementById(id) {
|
||||
return element(by.id(id)).atIndex(0);
|
||||
}
|
||||
|
||||
function elementByText(text) {
|
||||
return element(by.text(text)).atIndex(0);
|
||||
}
|
||||
|
||||
async function tapById(id) {
|
||||
await elementById(id).tap();
|
||||
}
|
||||
|
||||
async function tapByText(text) {
|
||||
await elementByText(text).tap();
|
||||
}
|
||||
|
||||
async function visibleByText(text) {
|
||||
await expect(elementByText(text)).toBeVisible();
|
||||
}
|
||||
|
||||
async function visibleById(id) {
|
||||
await expect(elementById(id)).toBeVisible();
|
||||
}
|
||||
|
||||
async function notVisibleById(id) {
|
||||
await expect(elementById(id)).not.toBeVisible();
|
||||
}
|
||||
|
||||
async function notVisibleByText(text) {
|
||||
await expect(elementByText(text)).not.toBeVisible();
|
||||
}
|
||||
|
||||
async function exitEditor() {
|
||||
await _device.pressBack();
|
||||
await _device.pressBack();
|
||||
}
|
||||
|
||||
async function createNote(_title, _body) {
|
||||
let title = _title || "Test note description that ";
|
||||
let body =
|
||||
_body ||
|
||||
"Test note description that is very long and should not fit in text.";
|
||||
await tapById(notesnook.buttons.add);
|
||||
let webview = web(by.id(notesnook.editor.id));
|
||||
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
|
||||
await webview.element(by.web.className("ProseMirror")).tap();
|
||||
await webview.element(by.web.className("ProseMirror")).typeText(body, true);
|
||||
await exitEditor();
|
||||
await waitFor(element(by.text(body)))
|
||||
.toBeVisible()
|
||||
.withTimeout(500);
|
||||
|
||||
return { title, body };
|
||||
}
|
||||
|
||||
async function openSideMenu() {
|
||||
let menu = elementById(notesnook.ids.default.header.buttons.left);
|
||||
await menu.tap();
|
||||
}
|
||||
|
||||
async function navigate(screen) {
|
||||
let menu = elementById(notesnook.ids.default.header.buttons.left);
|
||||
await waitFor(menu).toBeVisible().withTimeout(300);
|
||||
await menu.tap();
|
||||
|
||||
await waitFor(elementByText(screen)).toBeVisible().withTimeout(300);
|
||||
await elementByText(screen).tap();
|
||||
}
|
||||
|
||||
const testvars = {
|
||||
isFirstTest: true
|
||||
};
|
||||
|
||||
async function prepare() {
|
||||
await device.disableSynchronization();
|
||||
if (testvars.isFirstTest) {
|
||||
testvars.isFirstTest = false;
|
||||
return await LaunchApp();
|
||||
}
|
||||
await device.reverseTcpPort(8081);
|
||||
await device.uninstallApp();
|
||||
await device.installApp();
|
||||
await device.launchApp({ newInstance: true });
|
||||
await LaunchApp();
|
||||
}
|
||||
|
||||
async function matchSnapshot(element, name) {
|
||||
let path = await element.takeScreenshot(name);
|
||||
const bitmapBuffer = readFileSync(path);
|
||||
jestExpect(bitmapBuffer).toMatchImageSnapshot({
|
||||
failureThreshold: 200,
|
||||
failureThresholdType: "pixel"
|
||||
});
|
||||
}
|
||||
|
||||
async function createNotebook(title = "Notebook 1", description = true) {
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
title
|
||||
);
|
||||
if (description) {
|
||||
await elementById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).typeText(`Description of ${title}`);
|
||||
}
|
||||
await tapByText("Add");
|
||||
await sleep(300);
|
||||
}
|
||||
|
||||
export {
|
||||
matchSnapshot,
|
||||
createNotebook,
|
||||
prepare,
|
||||
LaunchApp,
|
||||
createNote,
|
||||
navigate,
|
||||
openSideMenu,
|
||||
notVisibleById,
|
||||
notVisibleByText,
|
||||
visibleById,
|
||||
visibleByText,
|
||||
tapById,
|
||||
tapByText,
|
||||
elementByText,
|
||||
elementById,
|
||||
sleep,
|
||||
exitEditor
|
||||
};
|
||||
149
apps/mobile/e2e/tests/utils.ts
Normal file
149
apps/mobile/e2e/tests/utils.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
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 { expect as jestExpect } from "@jest/globals";
|
||||
import { device as _device, expect } from "detox";
|
||||
import { readFileSync } from "fs";
|
||||
import { toMatchImageSnapshot } from "jest-image-snapshot";
|
||||
import type { RouteName } from "../../app/stores/use-navigation-store";
|
||||
import { notesnook } from "../test.ids";
|
||||
jestExpect.extend({ toMatchImageSnapshot });
|
||||
|
||||
const testvars = {
|
||||
isFirstTest: true
|
||||
};
|
||||
|
||||
class Element {
|
||||
element: Detox.NativeElement;
|
||||
constructor(public type: "id" | "text", public value: string) {
|
||||
if (type == "id") {
|
||||
this.element = element(by.id(value)).atIndex(0);
|
||||
} else {
|
||||
this.element = element(by.text(value)).atIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(timeout?: number) {
|
||||
return waitFor(this.element)
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout || 500);
|
||||
}
|
||||
|
||||
isNotVisible(timeout?: number) {
|
||||
return waitFor(this.element)
|
||||
.not.toBeVisible()
|
||||
.withTimeout(timeout || 500);
|
||||
}
|
||||
|
||||
async waitAndTap(timeout?: number) {
|
||||
await waitFor(this.element)
|
||||
.toBeVisible()
|
||||
.withTimeout(timeout || 500);
|
||||
await this.element.tap();
|
||||
}
|
||||
|
||||
tap(point?: Detox.Point2D): Promise<void> {
|
||||
return this.element.tap(point);
|
||||
}
|
||||
|
||||
static fromId(id: string) {
|
||||
return new Element("id", id);
|
||||
}
|
||||
static fromText(text: string) {
|
||||
return new Element("text", text);
|
||||
}
|
||||
}
|
||||
|
||||
const Tests = {
|
||||
awaitLaunch: async () => {
|
||||
await waitFor(element(by.id(notesnook.ids.default.root)))
|
||||
.toBeVisible()
|
||||
.withTimeout(500);
|
||||
},
|
||||
sleep: (duration: number) => {
|
||||
return new Promise((resolve) =>
|
||||
setTimeout(() => {
|
||||
resolve(undefined);
|
||||
}, duration)
|
||||
);
|
||||
},
|
||||
fromId: Element.fromId,
|
||||
fromText: Element.fromText,
|
||||
async exitEditor() {
|
||||
await _device.pressBack();
|
||||
await _device.pressBack();
|
||||
},
|
||||
async createNote(_title?: string, _body?: string) {
|
||||
let title = _title || "Test note description that ";
|
||||
let body =
|
||||
_body ||
|
||||
"Test note description that is very long and should not fit in text.";
|
||||
await Tests.fromId(notesnook.buttons.add).tap();
|
||||
await expect(web().element(by.web.className("ProseMirror"))).toExist();
|
||||
// await web().element(by.web.className("ProseMirror")).tap();
|
||||
await web().element(by.web.className("ProseMirror")).typeText(body, true);
|
||||
await Tests.exitEditor();
|
||||
await Tests.fromText(body).isVisible();
|
||||
return { title, body };
|
||||
},
|
||||
async navigate(screen: RouteName | ({} & string)) {
|
||||
let menu = Tests.fromId(notesnook.ids.default.header.buttons.left);
|
||||
await menu.waitAndTap();
|
||||
await Tests.fromText(screen).waitAndTap();
|
||||
},
|
||||
async openSideMenu() {
|
||||
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
|
||||
},
|
||||
async prepare() {
|
||||
await device.disableSynchronization();
|
||||
if (testvars.isFirstTest) {
|
||||
testvars.isFirstTest = false;
|
||||
return await Tests.awaitLaunch();
|
||||
}
|
||||
await device.reverseTcpPort(8081);
|
||||
await device.uninstallApp();
|
||||
await device.installApp();
|
||||
await device.launchApp({ newInstance: true });
|
||||
await Tests.awaitLaunch();
|
||||
},
|
||||
async createNotebook(title = "Notebook 1", description = true) {
|
||||
await Tests.sleep(1000);
|
||||
const titleInput = Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.title
|
||||
);
|
||||
await titleInput.isVisible();
|
||||
await titleInput.element.typeText(title);
|
||||
if (description) {
|
||||
await Tests.fromId(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).element.typeText(`Description of ${title}`);
|
||||
}
|
||||
await Tests.fromText("Add").waitAndTap();
|
||||
},
|
||||
async matchSnapshot(element: Element, name: string) {
|
||||
let path = await element.element.takeScreenshot(name);
|
||||
const bitmapBuffer = readFileSync(path);
|
||||
(jestExpect(bitmapBuffer) as any).toMatchImageSnapshot({
|
||||
failureThreshold: 200,
|
||||
failureThresholdType: "pixel"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { Element, Tests };
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
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 { notesnook } from "../test.ids";
|
||||
import {
|
||||
tapById,
|
||||
elementById,
|
||||
visibleByText,
|
||||
tapByText,
|
||||
createNote,
|
||||
prepare,
|
||||
visibleById,
|
||||
matchSnapshot,
|
||||
notVisibleById,
|
||||
navigate
|
||||
} from "./utils";
|
||||
import { sleep } from "./utils";
|
||||
|
||||
async function lockNote() {
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-lock-unlock");
|
||||
await sleep(1000);
|
||||
await visibleByText("Lock");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
|
||||
await tapByText("Lock");
|
||||
await sleep(500);
|
||||
await visibleById("note-locked-icon");
|
||||
}
|
||||
|
||||
async function removeFromVault() {
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-lock-unlock");
|
||||
await sleep(1000);
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await tapByText("Unlock");
|
||||
await sleep(1000);
|
||||
await notVisibleById("note-locked-icon");
|
||||
}
|
||||
|
||||
async function openLockedNote(pwd) {
|
||||
await tapById(notesnook.ids.note.get(1));
|
||||
await sleep(1000);
|
||||
await visibleByText("Open");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText(pwd || "1234");
|
||||
await tapByText("Open");
|
||||
await sleep(3000);
|
||||
await matchSnapshot(elementById("editor-wrapper"), "note-after-vault-unlock");
|
||||
}
|
||||
|
||||
async function goToPrivacySecuritySettings() {
|
||||
await navigate("Settings");
|
||||
await tapByText("Vault");
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
describe("VAULT", () => {
|
||||
it("Create vault from settings", async () => {
|
||||
await prepare();
|
||||
await goToPrivacySecuritySettings();
|
||||
await tapByText("Create vault");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
|
||||
await tapByText("Create");
|
||||
await sleep(500);
|
||||
await visibleByText("Clear vault");
|
||||
});
|
||||
|
||||
it("Change vault password", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await tapByText("Change vault password");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await elementById(notesnook.ids.dialogs.vault.changePwd).typeText("2362");
|
||||
await tapByText("Change");
|
||||
await sleep(2000);
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await openLockedNote("2362");
|
||||
});
|
||||
|
||||
it("Delete vault", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await tapByText("Delete vault");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await tapByText("Delete");
|
||||
await sleep(500);
|
||||
await visibleByText("Create vault");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await visibleById(notesnook.listitem.menu);
|
||||
});
|
||||
|
||||
it("Delete vault with locked notes", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await tapByText("Delete vault");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await tapByText("Delete all notes");
|
||||
await tapByText("Delete");
|
||||
await sleep(500);
|
||||
await visibleByText("Create vault");
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await notVisibleById(notesnook.listitem.menu);
|
||||
});
|
||||
|
||||
it("Add a note to vault", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
await openLockedNote();
|
||||
});
|
||||
|
||||
it("Remove note from vault", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
await removeFromVault();
|
||||
});
|
||||
});
|
||||
143
apps/mobile/e2e/tests/vault.e2e.ts
Normal file
143
apps/mobile/e2e/tests/vault.e2e.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
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 { expect } from "detox";
|
||||
import { notesnook } from "../test.ids";
|
||||
import { Tests } from "./utils";
|
||||
|
||||
async function lockNote() {
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("icon-lock-unlock").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromText("Lock").isVisible();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Lock").waitAndTap();
|
||||
await Tests.fromId("note-locked-icon").isVisible();
|
||||
}
|
||||
|
||||
async function removeFromVault() {
|
||||
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId("icon-lock-unlock").waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
|
||||
await Tests.fromText("Unlock").waitAndTap();
|
||||
await Tests.fromId("note-locked-icon").isNotVisible();
|
||||
}
|
||||
|
||||
async function openLockedNote(pwd?: string) {
|
||||
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
|
||||
await Tests.sleep(500);
|
||||
await web()
|
||||
.element(by.web.name("password"))
|
||||
.typeText(pwd || "1234", false);
|
||||
await web().element(by.web.className("unlock-note")).tap();
|
||||
await Tests.sleep(500);
|
||||
await expect(web().element(by.web.className("unlock-note"))).not.toExist();
|
||||
}
|
||||
|
||||
async function goToPrivacySecuritySettings() {
|
||||
await Tests.navigate("Settings");
|
||||
await Tests.fromText("Vault").waitAndTap();
|
||||
}
|
||||
|
||||
describe("VAULT", () => {
|
||||
it("Create vault from settings", async () => {
|
||||
await Tests.prepare();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Create vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Create").waitAndTap();
|
||||
await Tests.fromText("Clear vault").isVisible();
|
||||
});
|
||||
|
||||
it("Change vault password", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Change vault password").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.changePwd).element.typeText(
|
||||
"2362"
|
||||
);
|
||||
await Tests.fromText("Change").waitAndTap();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.sleep(500);
|
||||
await openLockedNote("2362");
|
||||
});
|
||||
|
||||
it("Delete vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Delete vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.fromText("Create vault").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.listitem.menu).isVisible();
|
||||
});
|
||||
|
||||
it("Delete vault with locked notes", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await goToPrivacySecuritySettings();
|
||||
await Tests.fromText("Delete vault").waitAndTap();
|
||||
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
|
||||
"1234"
|
||||
);
|
||||
await Tests.fromText("Delete notes in this vault").waitAndTap();
|
||||
await Tests.fromText("Delete").waitAndTap();
|
||||
await Tests.fromText("Create vault").isVisible();
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await Tests.fromId(notesnook.listitem.menu).isNotVisible();
|
||||
});
|
||||
|
||||
it("Add a note to vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await openLockedNote();
|
||||
});
|
||||
|
||||
it.only("Remove note from vault", async () => {
|
||||
await Tests.prepare();
|
||||
await Tests.createNote();
|
||||
await lockNote();
|
||||
await removeFromVault();
|
||||
});
|
||||
});
|
||||
@@ -52,7 +52,7 @@ module.exports = {
|
||||
emulator: {
|
||||
type: 'android.emulator',
|
||||
device: {
|
||||
avdName: 'Pixel_5_API_31'
|
||||
avdName: 'Pixel_5_API_34'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
org.gradle.configuration-cache=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
|
||||
@@ -18,10 +18,10 @@ import { i18n } from "@lingui/core";
|
||||
import { ScriptManager, Script } from '@callstack/repack/client';
|
||||
import {
|
||||
messages as $en
|
||||
} from "@notesnook/intl/locales/$en.json";
|
||||
} from "@notesnook/intl/dist/locales/$en.json";
|
||||
import {
|
||||
messages as $pseudo
|
||||
} from "@notesnook/intl/locales/$pseudo-LOCALE.json";
|
||||
} from "@notesnook/intl/dist/locales/$pseudo-LOCALE.json";
|
||||
|
||||
i18n.load({
|
||||
en: __DEV__ ? $pseudo : $en
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.7",
|
||||
"@ammarahmed/react-native-share-extension": "^3.0.0",
|
||||
"@ammarahmed/react-native-sodium": "1.5.4",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"@callstack/repack": "^4.1.1",
|
||||
"@react-native-clipboard/clipboard": "^1.9.0",
|
||||
@@ -73,7 +71,10 @@
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-screenguard": "^1.0.0",
|
||||
"@formatjs/intl-locale": "4.0.0",
|
||||
"@formatjs/intl-pluralrules": "5.2.14"
|
||||
"@formatjs/intl-pluralrules": "5.2.14",
|
||||
"detox": "^20.27.6",
|
||||
"@ammarahmed/react-native-share-extension": "^2.6.0",
|
||||
"@ammarahmed/react-native-sodium": "1.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
@@ -86,7 +87,6 @@
|
||||
"@react-native/metro-config": "0.74.86",
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/jest": "^29.2.1",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-native": "^0.69.1",
|
||||
@@ -98,19 +98,13 @@
|
||||
"babel-loader": "^8.2.5",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"babel-plugin-transform-remove-console": "6.9.4",
|
||||
"detox": "^20.11.2",
|
||||
"eslint": "^8.19.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"expect": "^29.6.2",
|
||||
"jest": "^29.6.3",
|
||||
"jest-circus": "^29.6.2",
|
||||
"jest-image-snapshot": "^6.2.0",
|
||||
"@react-native/babel-preset": "0.74.86",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"prettier": "2.8.8",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-bundle-visualizer": "^3.1.1",
|
||||
@@ -118,10 +112,16 @@
|
||||
"react-refresh": "0.14.0",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"terser-webpack-plugin": "^5.3.5",
|
||||
"ts-jest": "^29.1.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack": "^5.88.2",
|
||||
"metro-react-native-babel-preset": "0.77.0",
|
||||
"acorn-import-attributes": "1.9.5"
|
||||
"acorn-import-attributes": "1.9.5",
|
||||
"expect": "^29.7.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"jest-image-snapshot": "^6.4.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"pixelmatch": "^6.0.0",
|
||||
"@types/jest": "^29.5.14"
|
||||
}
|
||||
}
|
||||
|
||||
515
apps/mobile/package-lock.json
generated
515
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/react-native-actions-shortcuts/android/build.gradle b/node_modules/react-native-actions-shortcuts/android/build.gradle
|
||||
index 19bd311..d15cdaf 100644
|
||||
index 19bd311..bc37238 100644
|
||||
--- a/node_modules/react-native-actions-shortcuts/android/build.gradle
|
||||
+++ b/node_modules/react-native-actions-shortcuts/android/build.gradle
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
@@ -7,7 +7,7 @@ index 19bd311..d15cdaf 100644
|
||||
buildToolsVersion getExtOrDefault('buildToolsVersion')
|
||||
defaultConfig {
|
||||
- minSdkVersion 16
|
||||
+ minSdkVersion 21
|
||||
+ minSdkVersion getExtOrDefault('minSdkVersion')
|
||||
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
diff --git a/node_modules/react-native-fingerprint-scanner/android/build.gradle b/node_modules/react-native-fingerprint-scanner/android/build.gradle
|
||||
index eaf3829..6c392ba 100644
|
||||
--- a/node_modules/react-native-fingerprint-scanner/android/build.gradle
|
||||
+++ b/node_modules/react-native-fingerprint-scanner/android/build.gradle
|
||||
@@ -49,5 +49,5 @@ dependencies {
|
||||
// 1.2.3 is the minimum version compatible with androidx.
|
||||
// See https://github.com/uccmawei/FingerprintIdentify/issues/74
|
||||
// (translation https://translate.google.com/translate?sl=zh-CN&tl=en&u=https://github.com/uccmawei/FingerprintIdentify/issues/74)
|
||||
- implementation "com.wei.android.lib:fingerprintidentify:${safeExtGet("fingerprintidentify", "1.2.6")}"
|
||||
+ implementation "com.github.uccmawei:FingerprintIdentify:1.2.6"
|
||||
}
|
||||
@@ -39,7 +39,7 @@ index 0ccfd23..7771e6f 100644
|
||||
s.preserve_paths = 'LICENSE', 'package.json'
|
||||
s.source_files = '**/*.{h,m}'
|
||||
diff --git a/node_modules/react-native-tooltips/android/build.gradle b/node_modules/react-native-tooltips/android/build.gradle
|
||||
index 978045f..401b458 100644
|
||||
index 978045f..59c64f1 100644
|
||||
--- a/node_modules/react-native-tooltips/android/build.gradle
|
||||
+++ b/node_modules/react-native-tooltips/android/build.gradle
|
||||
@@ -3,8 +3,9 @@ buildscript {
|
||||
@@ -53,24 +53,36 @@ index 978045f..401b458 100644
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -15,12 +16,12 @@ buildscript {
|
||||
@@ -14,18 +15,19 @@ buildscript {
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
-android {
|
||||
- compileSdkVersion 29
|
||||
- buildToolsVersion '29.0.2'
|
||||
+ compileSdkVersion 31
|
||||
+ buildToolsVersion '31.0.0'
|
||||
|
||||
+def safeExtGet(prop, fallback) {
|
||||
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
+}
|
||||
+
|
||||
+android {
|
||||
+ compileSdkVersion safeExtGet('compileSdkVersion', 29)
|
||||
+ buildToolsVersion safeExtGet('buildToolsVersion', '29.0.2')
|
||||
defaultConfig {
|
||||
- minSdkVersion 16
|
||||
- targetSdkVersion 29
|
||||
+ minSdkVersion 21
|
||||
+ targetSdkVersion 31
|
||||
+ minSdkVersion safeExtGet('minSdkVersion', 21)
|
||||
+ targetSdkVersion safeExtGet('targetSdkVersion', 29)
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
- versionName "1.0"
|
||||
- }
|
||||
- lintOptions {
|
||||
- abortOnError false
|
||||
+ versionName "0.1"
|
||||
}
|
||||
@@ -36,5 +37,5 @@ repositories {
|
||||
}
|
||||
|
||||
@@ -36,5 +38,5 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
@@ -104,7 +116,7 @@ index fb96466..75a6fdb 100644
|
||||
|
||||
@interface TooltipDelegate : NSObject <SexyTooltipDelegate>
|
||||
|
||||
@@ -7,9 +9,15 @@ @interface TooltipDelegate : NSObject <SexyTooltipDelegate>
|
||||
@@ -7,9 +9,15 @@
|
||||
@end
|
||||
|
||||
@implementation TooltipDelegate
|
||||
@@ -120,7 +132,7 @@ index fb96466..75a6fdb 100644
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,77 +25,120 @@ - (void)tooltipDidDismiss:(SexyTooltip *)tooltip {
|
||||
@@ -17,77 +25,120 @@
|
||||
|
||||
@implementation RNTooltips
|
||||
|
||||
|
||||
@@ -73,8 +73,6 @@ test("creating a color shouldn't be possible on basic plan", async ({
|
||||
await note?.contextMenu.newColor({ title: "red", color: "#ff0000" });
|
||||
|
||||
expect(
|
||||
await app.toasts.waitForToast(
|
||||
"Please upgrade your account to Pro to add colors."
|
||||
)
|
||||
await app.toasts.waitForToast("Upgrade to Notesnook Pro to add colors.")
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ export class SettingsViewModel {
|
||||
}
|
||||
|
||||
async isLoggedIn() {
|
||||
const item = await this.navigation.findItem("Subscription");
|
||||
const item = await this.navigation.findItem("Subscription details");
|
||||
return !!(await item?.getTitle());
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ test("delete a notebook", async ({ page }) => {
|
||||
await notebook?.moveToTrash();
|
||||
|
||||
expect(await notebook?.isPresent()).toBe(false);
|
||||
expect(await app.toasts.waitForToast("1 notebook moved to trash")).toBe(true);
|
||||
expect(await app.toasts.waitForToast("Notebook moved to trash")).toBe(true);
|
||||
const trash = await app.goToTrash();
|
||||
expect(await trash.findItem(NOTEBOOK.title)).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ test("delete a note", async ({ page }) => {
|
||||
|
||||
await note?.contextMenu.moveToTrash();
|
||||
|
||||
expect(await app.toasts.waitForToast("1 note moved to trash")).toBe(true);
|
||||
expect(await app.toasts.waitForToast("Note moved to trash")).toBe(true);
|
||||
expect(await note?.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ test("restore a note", async ({ page }) => {
|
||||
const trashItem = await trash.findItem(NOTE.title);
|
||||
await trashItem?.restore();
|
||||
|
||||
expect(await app.toasts.waitForToast("1 item restored")).toBe(true);
|
||||
expect(await app.toasts.waitForToast("Item restored")).toBe(true);
|
||||
await app.goToNotes();
|
||||
await notes.waitForItem(NOTE.title);
|
||||
const restoredNote = await notes.findNote(NOTE);
|
||||
@@ -78,9 +78,7 @@ test("permanently delete a note", async ({ page }) => {
|
||||
const trashItem = await trash.findItem(NOTE.title);
|
||||
await trashItem?.delete();
|
||||
|
||||
expect(await app.toasts.waitForToast("1 item permanently deleted")).toBe(
|
||||
true
|
||||
);
|
||||
expect(await app.toasts.waitForToast("Item permanently deleted")).toBe(true);
|
||||
expect(await trash.findItem(NOTE.title)).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ test("delete a tag", async ({ page }) => {
|
||||
|
||||
await tag?.delete();
|
||||
|
||||
expect(await app.toasts.waitForToast("1 tag deleted")).toBe(true);
|
||||
expect(await app.toasts.waitForToast("Tag deleted")).toBe(true);
|
||||
expect(await tags?.findItem(TAG)).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -235,9 +235,7 @@ test("creating more than 5 tags shouldn't be possible on basic plan", async ({
|
||||
|
||||
const result = await Promise.race([
|
||||
tags.createItem({ title: "tag6" }),
|
||||
app.toasts.waitForToast(
|
||||
"Please upgrade your account to Pro to add more tags."
|
||||
)
|
||||
app.toasts.waitForToast("Upgrade to Notesnook Pro to create more tags.")
|
||||
]);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ export default function AppEffects({ setShow }: AppEffectsProps) {
|
||||
sentence = strings.upgradeToProToUseFeature("color");
|
||||
break;
|
||||
case CHECK_IDS.noteTag:
|
||||
sentence = strings.upgradeToProToUseFeature("tag");
|
||||
sentence = strings.upgradeToProToUseFeature("tags");
|
||||
break;
|
||||
case CHECK_IDS.notebookAdd:
|
||||
sentence = strings.upgradeToProToUseFeature("notebook");
|
||||
|
||||
@@ -44,7 +44,6 @@ import { ReminderDialog } from "../dialogs/reminder-dialog";
|
||||
import { Cipher, SerializedKey } from "@notesnook/crypto";
|
||||
import { ChunkedStream } from "../utils/streams/chunked-stream";
|
||||
import { isFeatureSupported } from "../utils/feature-check";
|
||||
import { NNCrypto } from "../interfaces/nncrypto";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const CREATE_BUTTON_MAP = {
|
||||
@@ -215,7 +214,7 @@ export async function restoreBackupFile(backupFile: File) {
|
||||
} else {
|
||||
const error = await TaskManager.startTask<Error | void>({
|
||||
title: strings.restoringBackup(),
|
||||
subtitle: `${strings.restoringBackupDesc()}...`,
|
||||
subtitle: strings.restoringBackupDesc(),
|
||||
type: "modal",
|
||||
action: async (report) => {
|
||||
let cachedPassword: string | undefined = undefined;
|
||||
@@ -344,14 +343,14 @@ async function restoreWithProgress(
|
||||
current: number;
|
||||
}) => {
|
||||
report({
|
||||
text: `Restoring ${collection}...`,
|
||||
text: strings.restoringCollection(collection),
|
||||
current,
|
||||
total
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
report({ text: `Restoring...` });
|
||||
report({ text: strings.restoring() });
|
||||
return restore(backup, password, key);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,49 +48,64 @@ async function moveNotesToTrash(ids: string[], confirm = true) {
|
||||
await TaskManager.startTask({
|
||||
type: "status",
|
||||
id: "deleteNotes",
|
||||
title: strings.deletingItems("note", items.length),
|
||||
title: strings.inProgressActions.deleting.note(items.length),
|
||||
action: async (report) => {
|
||||
report({
|
||||
text: `${strings.inProgressAction("note", items.length, "deleting")}...`
|
||||
text: strings.inProgressActions.deleting.note(items.length)
|
||||
});
|
||||
await noteStore.delete(...items);
|
||||
}
|
||||
});
|
||||
|
||||
showToast("success", strings.movedToTrash("note", items.length));
|
||||
showToast("success", strings.actions.movedToTrash.note(items.length));
|
||||
}
|
||||
|
||||
async function moveNotebooksToTrash(ids: string[]) {
|
||||
if (!ids.length) return;
|
||||
|
||||
const isMultiselect = ids.length > 1;
|
||||
if (isMultiselect) {
|
||||
if (!(await showMultiDeleteConfirmation(ids.length))) return;
|
||||
const result = await ConfirmDialog.show({
|
||||
title: strings.doActions.delete.notebook(ids.length),
|
||||
positiveButtonText: strings.yes(),
|
||||
negativeButtonText: strings.no(),
|
||||
checks: {
|
||||
deleteContainingNotes: {
|
||||
text: strings.deleteContainingNotes(ids.length)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result) return;
|
||||
|
||||
if (result.deleteContainingNotes) {
|
||||
await Multiselect.moveNotesToTrash(
|
||||
Array.from(
|
||||
new Set(
|
||||
(await Promise.all(ids.map((id) => db.notebooks.notes(id)))).flat()
|
||||
)
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
await TaskManager.startTask({
|
||||
type: "status",
|
||||
id: "deleteNotebooks",
|
||||
title: strings.deletingItems("notebook", ids.length),
|
||||
title: strings.inProgressActions.deleting.notebook(ids.length),
|
||||
action: async (report) => {
|
||||
report({
|
||||
text: `${strings.inProgressAction(
|
||||
"notebook",
|
||||
ids.length,
|
||||
"deleting"
|
||||
)}...`
|
||||
text: strings.inProgressActions.deleting.notebook(ids.length)
|
||||
});
|
||||
await notebookStore.delete(...ids);
|
||||
}
|
||||
});
|
||||
|
||||
showToast("success", strings.movedToTrash("notebook", ids.length));
|
||||
showToast("success", strings.actions.movedToTrash.notebook(ids.length));
|
||||
}
|
||||
|
||||
async function deleteAttachments(ids: string[]) {
|
||||
if (
|
||||
!(await ConfirmDialog.show({
|
||||
title: strings.doAction("attachment", ids.length, "permanentlyDelete"),
|
||||
title: strings.doActions.permanentlyDelete.attachment(ids.length),
|
||||
message: strings.irreverisibleAction(),
|
||||
negativeButtonText: strings.no(),
|
||||
positiveButtonText: strings.yes()
|
||||
@@ -101,7 +116,7 @@ async function deleteAttachments(ids: string[]) {
|
||||
await TaskManager.startTask({
|
||||
type: "status",
|
||||
id: "deleteAttachments",
|
||||
title: strings.deletingItems("attachment", ids.length),
|
||||
title: strings.inProgressActions.deleting.attachment(ids.length),
|
||||
action: async (report) => {
|
||||
for (let i = 0; i < ids.length; ++i) {
|
||||
const id = ids[i];
|
||||
@@ -109,11 +124,7 @@ async function deleteAttachments(ids: string[]) {
|
||||
if (!attachment) continue;
|
||||
|
||||
report({
|
||||
text: `${strings.inProgressAction(
|
||||
"attachment",
|
||||
ids.length,
|
||||
"deleting"
|
||||
)}...`,
|
||||
text: strings.inProgressActions.deleting.attachment(ids.length),
|
||||
current: i,
|
||||
total: ids.length
|
||||
});
|
||||
@@ -121,7 +132,7 @@ async function deleteAttachments(ids: string[]) {
|
||||
}
|
||||
}
|
||||
});
|
||||
showToast("success", strings.deleted("attachment", ids.length));
|
||||
showToast("success", strings.actions.deleted.attachment(ids.length));
|
||||
}
|
||||
|
||||
async function moveRemindersToTrash(ids: string[]) {
|
||||
@@ -133,20 +144,16 @@ async function moveRemindersToTrash(ids: string[]) {
|
||||
await TaskManager.startTask({
|
||||
type: "status",
|
||||
id: "deleteReminders",
|
||||
title: strings.deletingItems("reminder", ids.length),
|
||||
title: strings.inProgressActions.deleting.reminder(ids.length),
|
||||
action: async (report) => {
|
||||
report({
|
||||
text: `${strings.inProgressAction(
|
||||
"reminder",
|
||||
ids.length,
|
||||
"deleting"
|
||||
)}...`
|
||||
text: strings.inProgressActions.deleting.reminder(ids.length)
|
||||
});
|
||||
await reminderStore.delete(...ids);
|
||||
}
|
||||
});
|
||||
|
||||
showToast("success", strings.deleted("reminder", ids.length));
|
||||
showToast("success", strings.actions.deleted.reminder(ids.length));
|
||||
}
|
||||
|
||||
async function deleteTags(ids: string[]) {
|
||||
@@ -161,7 +168,7 @@ async function deleteTags(ids: string[]) {
|
||||
title: "Deleting tags",
|
||||
action: async (report) => {
|
||||
report({
|
||||
text: `${strings.inProgressAction("tag", ids.length, "deleting")}...`
|
||||
text: strings.inProgressActions.deleting.tag(ids.length)
|
||||
});
|
||||
for (const id of ids) {
|
||||
await db.tags.remove(id);
|
||||
@@ -172,7 +179,7 @@ async function deleteTags(ids: string[]) {
|
||||
}
|
||||
});
|
||||
|
||||
showToast("success", strings.deleted("tag", ids.length));
|
||||
showToast("success", strings.actions.deleted.tag(ids.length));
|
||||
}
|
||||
|
||||
export const Multiselect = {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
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 { db } from "./db";
|
||||
import { store as notestore } from "../stores/note-store";
|
||||
import { store as nbstore } from "../stores/notebook-store";
|
||||
import { showToast } from "../utils/toast";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
async function showUnpinnedToast(itemId, itemType) {
|
||||
const messageText = strings.action(itemType, 1, "unpinned");
|
||||
const undoAction = async () => {
|
||||
toast.hide();
|
||||
if (itemType === "note") await notestore.pin(itemId);
|
||||
else if (itemType === "notebook") await nbstore.pin(itemId);
|
||||
};
|
||||
let actions = [{ text: "Undo", onClick: undoAction }];
|
||||
var toast = showToast("success", messageText, actions);
|
||||
}
|
||||
|
||||
function showItemDeletedToast(item) {
|
||||
const messageText = strings.action(item.type, 1, "deleted");
|
||||
const undoAction = async () => {
|
||||
toast.hide();
|
||||
let trashItem = db.trash.all.find((i) => i.id === item.id);
|
||||
if (!trashItem) return;
|
||||
await db.trash.restore(trashItem.id);
|
||||
nbstore.refresh();
|
||||
notestore.refresh();
|
||||
};
|
||||
let actions = [{ text: "Undo", onClick: undoAction }];
|
||||
var toast = showToast("success", messageText, actions);
|
||||
}
|
||||
|
||||
async function showUndoableToast(message, onAction, onPermanentAction, onUndo) {
|
||||
await onAction();
|
||||
const timeoutId = setTimeout(onPermanentAction, 5000);
|
||||
const undoAction = async () => {
|
||||
clearTimeout(timeoutId);
|
||||
toast.hide();
|
||||
onUndo();
|
||||
};
|
||||
let actions = [{ text: strings.undo(), onClick: undoAction }];
|
||||
var toast = showToast("success", message, actions);
|
||||
}
|
||||
|
||||
export { showUnpinnedToast, showItemDeletedToast, showUndoableToast };
|
||||
@@ -303,7 +303,7 @@ const AttachmentMenuItems: (
|
||||
icon: Rename.path,
|
||||
onClick: async () => {
|
||||
const newName = await PromptDialog.show({
|
||||
title: strings.doAction("attachment", 1, "rename"),
|
||||
title: strings.doActions.rename.attachment(1),
|
||||
description: attachment.filename,
|
||||
defaultValue: attachment.filename
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ 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 { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { SaveState, useEditorStore } from "../../stores/editor-store";
|
||||
import { Loading, Saved, NotSaved } from "../icons";
|
||||
import { useNoteStatistics } from "./manager";
|
||||
@@ -58,8 +58,8 @@ function EditorFooter() {
|
||||
variant="subBody"
|
||||
sx={{ color: "paragraph" }}
|
||||
>
|
||||
{words.total + " " + strings.words()}
|
||||
{words.selected ? ` (${words.selected} ${strings.selected()})` : ""}
|
||||
{strings.totalWords(words.total)}
|
||||
{words.selected ? ` (${strings.selectedWords(words.selected)})` : ""}
|
||||
</Text>
|
||||
{dateEdited > 0 ? (
|
||||
<Text
|
||||
|
||||
@@ -33,7 +33,7 @@ function EditorLoading({ text }: { text?: string }) {
|
||||
>
|
||||
<Loading color="accent" sx={{ mt: 2 }} />
|
||||
<Text variant="body" mt={2} sx={{ textAlign: "center" }}>
|
||||
{text || `${strings.loadingEditor()}. ${strings.pleaseWait()}...`}
|
||||
{text || strings.loadingEditor()}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -368,12 +368,12 @@ function NavigationMenu(props: NavigationMenuProps) {
|
||||
{
|
||||
type: "button",
|
||||
key: "rename-color",
|
||||
title: strings.doAction("color", 1, "rename"),
|
||||
title: strings.renameColor(),
|
||||
onClick: () => RenameColorDialog.show(color)
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
key: strings.doAction("color", 1, "remove"),
|
||||
key: "remove-color",
|
||||
title: strings.removeColor(),
|
||||
onClick: async () => {
|
||||
await db.colors.remove(color.id);
|
||||
@@ -432,7 +432,7 @@ function NavigationMenu(props: NavigationMenuProps) {
|
||||
{
|
||||
type: "button",
|
||||
key: "removeshortcut",
|
||||
title: strings.doAction("shortcut", 1, "remove"),
|
||||
title: strings.doActions.remove.shortcut(1),
|
||||
onClick: async () => {
|
||||
await db.shortcuts.remove(item.id);
|
||||
refreshNavItems();
|
||||
|
||||
@@ -244,7 +244,7 @@ function Note(props: NoteProps) {
|
||||
data-test-id={`tag-item`}
|
||||
key={tag.id}
|
||||
variant="anchor"
|
||||
title={`${strings.goTo()} #${tag.title}`}
|
||||
title={strings.goToTag(tag.title)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!tag.id)
|
||||
@@ -721,7 +721,7 @@ function tagsMenuItems(ids: string[]): MenuItem[] {
|
||||
{
|
||||
type: "button",
|
||||
key: "assign-tags",
|
||||
title: `${strings.assignTo()}...`,
|
||||
title: strings.assignTo(),
|
||||
icon: Plus.path,
|
||||
onClick: () => AddTagsDialog.show({ noteIds: ids })
|
||||
},
|
||||
@@ -815,7 +815,7 @@ async function copyNote(noteId: string, format: "md" | "txt") {
|
||||
disableTemplate: true,
|
||||
unlockVault: Vault.unlockVault
|
||||
});
|
||||
if (!result) throw new Error(`${strings.couldNotConvertNote(format)}.`);
|
||||
if (!result) throw new Error(strings.couldNotConvertNote(format));
|
||||
|
||||
await navigator.clipboard.writeText(result);
|
||||
showToast("success", strings.noteCopied());
|
||||
|
||||
@@ -37,13 +37,11 @@ import {
|
||||
import { hashNavigate, navigate } from "../../navigation";
|
||||
import IconTag from "../icon-tag";
|
||||
import { Multiselect } from "../../common/multi-select";
|
||||
import { pluralize } from "@notesnook/common";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { MenuItem } from "@notesnook/ui";
|
||||
import { Notebook as NotebookType } from "@notesnook/core";
|
||||
import { handleDrop } from "../../common/drop-handler";
|
||||
import { useDragHandler } from "../../hooks/use-drag-handler";
|
||||
import { ConfirmDialog } from "../../dialogs/confirm";
|
||||
import { useStore as useSelectionStore } from "../../stores/selection-store";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
@@ -210,30 +208,7 @@ export const notebookMenuItems: (
|
||||
title: strings.moveToTrash(),
|
||||
variant: "dangerous",
|
||||
icon: Trash.path,
|
||||
onClick: async () => {
|
||||
const result = await ConfirmDialog.show({
|
||||
title: `Delete ${pluralize(ids.length, "notebook")}?`,
|
||||
positiveButtonText: strings.yes(),
|
||||
negativeButtonText: strings.no(),
|
||||
checks: {
|
||||
deleteContainingNotes: {
|
||||
text: `Move all notes in ${
|
||||
ids.length > 1 ? "these notebooks" : "this notebook"
|
||||
} to trash`
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (result) {
|
||||
if (result.deleteContainingNotes) {
|
||||
await Multiselect.moveNotesToTrash(
|
||||
await db.notebooks.notes(notebook.id),
|
||||
false
|
||||
);
|
||||
}
|
||||
await Multiselect.moveNotebooksToTrash(ids);
|
||||
}
|
||||
},
|
||||
onClick: () => Multiselect.moveNotebooksToTrash(ids),
|
||||
multiSelect: true
|
||||
}
|
||||
];
|
||||
|
||||
@@ -21,7 +21,6 @@ import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { TipContext, useTip } from "../../hooks/use-tip";
|
||||
import { Info, Sync } from "../icons";
|
||||
import { useStore as useAppStore } from "../../stores/app-store";
|
||||
import { toTitleCase } from "@notesnook/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type PlaceholderProps = { context: TipContext; text?: string };
|
||||
@@ -54,13 +53,13 @@ function Placeholder(props: PlaceholderProps) {
|
||||
>
|
||||
<Sync color="accent" size={12} sx={{ mr: "small" }} />
|
||||
<Text variant="subBody" sx={{ fontSize: 10 }} color="accent">
|
||||
{strings.syncingYour(context)}
|
||||
{strings.syncingYourNotes()}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Text variant="subBody" sx={{ fontSize: "body", mt: 1 }}>
|
||||
{toTitleCase(syncStatus.type || "sync")}ing {syncStatus.progress}{" "}
|
||||
{strings.items()}
|
||||
{strings.networkProgress(syncStatus.type || "sync")}{" "}
|
||||
{syncStatus.progress} {strings.items()}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -203,12 +203,12 @@ function PublishView(props: PublishViewProps) {
|
||||
password
|
||||
});
|
||||
setPublishId(publishId);
|
||||
showToast("success", strings.action("note", 1, "published"));
|
||||
showToast("success", strings.actions.published.note(1));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.actionError("note", 1, "published")}: ${
|
||||
`${strings.actionErrors.published.note(1)}: ${
|
||||
(e as Error).message
|
||||
}`
|
||||
);
|
||||
@@ -228,12 +228,12 @@ function PublishView(props: PublishViewProps) {
|
||||
await unpublishNote(note.id);
|
||||
setPublishId(undefined);
|
||||
onClose(true);
|
||||
showToast("success", strings.action("note", 1, "unpublished"));
|
||||
showToast("success", strings.actions.unpublished.note(1));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.actionError("note", 1, "unpublished")}: ` +
|
||||
`${strings.actionErrors.unpublished.note(1)}: ` +
|
||||
(e as Error).message
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -152,7 +152,7 @@ const menuItems: (reminder: ReminderType, items?: string[]) => MenuItem[] = (
|
||||
icon: Trash.path,
|
||||
onClick: async () => {
|
||||
ConfirmDialog.show({
|
||||
title: strings.doAction("reminder", items.length, "delete"),
|
||||
title: strings.doActions.delete.reminder(items.length),
|
||||
message: strings.irreverisibleAction(),
|
||||
positiveButtonText: strings.yes(),
|
||||
negativeButtonText: strings.no()
|
||||
|
||||
@@ -194,13 +194,11 @@ function statusToInfoText(status: UpdateStatus) {
|
||||
return type === "checking"
|
||||
? strings.checkingForUpdates()
|
||||
: type === "downloading"
|
||||
? `${Math.round(status.progress)}% ${strings.updating()}...`
|
||||
? strings.updating(Math.round(status.progress))
|
||||
: type === "completed"
|
||||
? `v${
|
||||
status.version
|
||||
} ${strings.network.downloaded()} (${strings.restartRequired()})`
|
||||
? strings.updateCompleted(status.version)
|
||||
: type === "available"
|
||||
? `v${status.version} ${strings.available()}`
|
||||
? strings.updateNewVersionAvailable(status.version)
|
||||
: "";
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ export const AddNotebookDialog = DialogManager.register(
|
||||
|
||||
showToast(
|
||||
"success",
|
||||
strings.action("notebook", 1, props.edit ? "edited" : "created")
|
||||
props.edit
|
||||
? strings.actions.edited.notebook(1)
|
||||
: strings.actions.created.notebook(1)
|
||||
);
|
||||
onClose(true);
|
||||
}, [props.notebook?.id, props.edit, onClose, parentId]);
|
||||
|
||||
@@ -191,7 +191,7 @@ export function CheckoutSideBar(props: SideBarProps) {
|
||||
if (e instanceof Error)
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.couldNotActivateTrial()}. ${strings.pleaseTryAgain()} Error: ${
|
||||
`${strings.couldNotActivateTrial()} ${strings.error()}: ${
|
||||
e.message
|
||||
}`
|
||||
);
|
||||
|
||||
@@ -26,7 +26,6 @@ import { db } from "../common/db";
|
||||
import { getChangelog } from "../utils/version";
|
||||
import { downloadUpdate } from "../utils/updater";
|
||||
import { ErrorText } from "../components/error-text";
|
||||
import { pluralize } from "@notesnook/common";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type Check = { text: string; default?: boolean };
|
||||
@@ -145,7 +144,7 @@ export const ConfirmDialog = DialogManager.register(function ConfirmDialog<
|
||||
|
||||
export function showMultiDeleteConfirmation(length: number) {
|
||||
return ConfirmDialog.show({
|
||||
title: strings.doAction("item", length, "delete"),
|
||||
title: strings.doActions.delete.item(length),
|
||||
message: strings.moveToTrashDesc(
|
||||
db.settings.getTrashCleanupInterval() || 7
|
||||
),
|
||||
@@ -156,7 +155,7 @@ export function showMultiDeleteConfirmation(length: number) {
|
||||
|
||||
export function showMultiPermanentDeleteConfirmation(length: number) {
|
||||
return ConfirmDialog.show({
|
||||
title: strings.doAction("item", length, "permanentlyDelete"),
|
||||
title: strings.doActions.permanentlyDelete.item(length),
|
||||
message: strings.irreverisibleAction(),
|
||||
positiveButtonText: strings.yes(),
|
||||
negativeButtonText: strings.no()
|
||||
|
||||
@@ -106,7 +106,7 @@ export const CreateTagDialog = {
|
||||
)
|
||||
return;
|
||||
|
||||
showToast("success", strings.action("tag", 1, "created"));
|
||||
showToast("success", strings.actions.created.tag(1));
|
||||
useTagStore.getState().refresh();
|
||||
})
|
||||
};
|
||||
@@ -114,7 +114,7 @@ export const CreateTagDialog = {
|
||||
export const EditTagDialog = {
|
||||
show: (tag: Tag) =>
|
||||
ItemDialog.show({
|
||||
title: strings.doAction("tag", 1, "edit"),
|
||||
title: strings.doActions.edit.tag(1),
|
||||
subtitle: strings.editingTagDesc(tag.title),
|
||||
defaultValue: tag.title
|
||||
}).then(async (title) => {
|
||||
@@ -126,7 +126,7 @@ export const EditTagDialog = {
|
||||
}))
|
||||
)
|
||||
return;
|
||||
showToast("success", strings.action("tag", 1, "edited"));
|
||||
showToast("success", strings.actions.edited.tag(1));
|
||||
await useTagStore.getState().refresh();
|
||||
await useNoteStore.getState().refresh();
|
||||
await useAppStore.getState().refreshNavItems();
|
||||
@@ -148,7 +148,7 @@ export const RenameColorDialog = {
|
||||
}))
|
||||
)
|
||||
return;
|
||||
showToast("success", strings.action("color", 1, "renamed"));
|
||||
showToast("success", strings.actions.renamed.color(1));
|
||||
useAppStore.getState().refreshNavItems();
|
||||
})
|
||||
};
|
||||
|
||||
@@ -55,13 +55,13 @@ export const MigrationDialog = DialogManager.register(function MigrationDialog(
|
||||
EVENTS.migrationProgress,
|
||||
({ collection, total, current }: MigrationProgressEvent) => {
|
||||
task({
|
||||
text: `${strings.processing()} ${collection}...`,
|
||||
text: strings.processingCollection(collection),
|
||||
current,
|
||||
total
|
||||
});
|
||||
}
|
||||
);
|
||||
task({ text: `Processing...` });
|
||||
task({ text: strings.processing() });
|
||||
try {
|
||||
await db.migrations.migrate();
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ import {
|
||||
TreeEnvironmentRef
|
||||
} from "react-complex-tree";
|
||||
import { FlexScrollContainer } from "../components/scroll-container";
|
||||
import { pluralize } from "@notesnook/common";
|
||||
import Field from "../components/field";
|
||||
import { AddNotebookDialog } from "./add-notebook-dialog";
|
||||
import { BaseDialogProps, DialogManager } from "../common/dialog-manager";
|
||||
@@ -153,11 +152,13 @@ export const MoveNoteDialog = DialogManager.register(function MoveNoteDialog({
|
||||
await noteStore.refresh();
|
||||
await notebookStore.refresh();
|
||||
|
||||
const stringified = stringifySelected(selected);
|
||||
if (stringified) {
|
||||
const added = selected.filter((a) => a.new && a.op === "add").length;
|
||||
const removed = selected.filter((a) => a.op === "remove").length;
|
||||
|
||||
if (added > 0 || removed > 0) {
|
||||
showToast(
|
||||
"success",
|
||||
`${strings.notes(noteIds.length)} ${stringified}`
|
||||
strings.assignedToNotebookMessage(noteIds.length, added, removed)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -562,37 +563,3 @@ function selectSingle(topic: Notebook, array: NotebookReference[]) {
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
function stringifySelected(suggestion: NotebookReference[]) {
|
||||
const added = suggestion.filter((a) => a.new && a.op === "add");
|
||||
// .map(resolveReference)
|
||||
// .filter(Boolean);
|
||||
const removed = suggestion.filter((a) => a.op === "remove");
|
||||
// .map(resolveReference)
|
||||
// .filter(Boolean);
|
||||
if (!added.length && !removed.length) return;
|
||||
|
||||
const parts = [];
|
||||
if (added.length > 0) parts.push(strings.addedToNotebook(added.length));
|
||||
// if (added.length >= 1) parts.push(added[0]);
|
||||
// if (added.length > 1) parts.push(`and ${added.length - 1} others`);
|
||||
|
||||
if (removed.length >= 1) {
|
||||
if (parts.length > 0) parts.push("&");
|
||||
parts.push(`removed from ${strings.removedFromNotebook(removed.length)}`);
|
||||
}
|
||||
// if (removed.length > 1) parts.push(`and ${removed.length - 1} others`);
|
||||
|
||||
return parts.join(" ") + ".";
|
||||
}
|
||||
|
||||
// function resolveReference(ref: NotebookReference): string | undefined {
|
||||
// const notebook = db.notebooks.notebook(ref.id);
|
||||
// if (!notebook) return undefined;
|
||||
|
||||
// // if (ref.topic) {
|
||||
// // return notebook.topics.topic(ref.topic)?._topic?.title;
|
||||
// // } else {
|
||||
// return notebook.title;
|
||||
// // }
|
||||
// }
|
||||
|
||||
@@ -17,12 +17,16 @@ 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 { createBackup, verifyAccount } from "../../common";
|
||||
import { createBackup, verifyAccount, importBackup } from "../../common";
|
||||
import { db } from "../../common/db";
|
||||
import { exportNotes } from "../../common/export";
|
||||
import { SettingsGroup } from "./types";
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useStore as useSettingStore } from "../../stores/setting-store";
|
||||
import { useStore as useAppStore } from "../../stores/app-store";
|
||||
import { useStore as useUserStore } from "../../stores/user-store";
|
||||
import { desktop } from "../../common/desktop-bridge";
|
||||
import { PATHS } from "@notesnook/desktop";
|
||||
|
||||
export const BackupExportSettings: SettingsGroup[] = [
|
||||
{
|
||||
@@ -51,6 +55,142 @@ export const BackupExportSettings: SettingsGroup[] = [
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "restore-backup",
|
||||
title: strings.restoreBackup(),
|
||||
description: strings.restoreBackupDesc(),
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
title: "Restore",
|
||||
action: async () => {
|
||||
if (await importBackup()) {
|
||||
await useAppStore.getState().refresh();
|
||||
}
|
||||
},
|
||||
variant: "secondary"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "auto-backup",
|
||||
title: strings.automaticBackups(),
|
||||
description: strings.automaticBackupsDesc(),
|
||||
// isHidden: () => !isUserPremium(),
|
||||
onStateChange: (listener) =>
|
||||
useSettingStore.subscribe((s) => s.backupReminderOffset, listener),
|
||||
components: [
|
||||
{
|
||||
type: "dropdown",
|
||||
options: [
|
||||
{ value: "0", title: strings.never(), premium: true },
|
||||
{ value: "1", title: strings.daily(), premium: true },
|
||||
{ value: "2", title: strings.weekly(), premium: true },
|
||||
{ value: "3", title: strings.monthly(), premium: true }
|
||||
],
|
||||
selectedOption: () =>
|
||||
useSettingStore.getState().backupReminderOffset.toString(),
|
||||
onSelectionChanged: async (value) => {
|
||||
const verified =
|
||||
useSettingStore.getState().encryptBackups ||
|
||||
(await verifyAccount());
|
||||
if (verified)
|
||||
useSettingStore
|
||||
.getState()
|
||||
.setBackupReminderOffset(parseInt(value));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "auto-backup-with-attachments",
|
||||
title: strings.automaticBackupsWithAttachments(),
|
||||
description: strings.automaticBackupsWithAttachmentsDesc().join("\n\n"),
|
||||
onStateChange: (listener) =>
|
||||
useSettingStore.subscribe(
|
||||
(s) => s.fullBackupReminderOffset,
|
||||
listener
|
||||
),
|
||||
components: [
|
||||
{
|
||||
type: "dropdown",
|
||||
options: [
|
||||
{ value: "0", title: strings.never(), premium: true },
|
||||
{ value: "1", title: strings.weekly(), premium: true },
|
||||
{ value: "2", title: strings.monthly(), premium: true }
|
||||
],
|
||||
selectedOption: () =>
|
||||
useSettingStore.getState().fullBackupReminderOffset.toString(),
|
||||
onSelectionChanged: async (value) => {
|
||||
const verified =
|
||||
useSettingStore.getState().encryptBackups ||
|
||||
(await verifyAccount());
|
||||
if (verified)
|
||||
useSettingStore
|
||||
.getState()
|
||||
.setFullBackupReminderOffset(parseInt(value));
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "encrypt-backups",
|
||||
title: strings.backupEncryption(),
|
||||
description: strings.backupEncryptionDesc(),
|
||||
isHidden: () => !useUserStore.getState().isLoggedIn,
|
||||
onStateChange: (listener) => {
|
||||
const subscriptions = [
|
||||
useUserStore.subscribe((s) => s.isLoggedIn, listener),
|
||||
useSettingStore.subscribe((s) => s.encryptBackups, listener)
|
||||
];
|
||||
return () => subscriptions.forEach((s) => s());
|
||||
},
|
||||
components: [
|
||||
{
|
||||
type: "toggle",
|
||||
isToggled: () =>
|
||||
!!useUserStore.getState().isLoggedIn &&
|
||||
useSettingStore.getState().encryptBackups,
|
||||
toggle: async () => {
|
||||
const verified =
|
||||
!useSettingStore.getState().encryptBackups ||
|
||||
(await verifyAccount());
|
||||
if (verified) useSettingStore.getState().toggleEncryptBackups();
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "backup-directory",
|
||||
title: strings.selectBackupDir(),
|
||||
description: strings.selectBackupDirDesc(),
|
||||
isHidden: () => !IS_DESKTOP_APP,
|
||||
components: [
|
||||
{
|
||||
type: "button",
|
||||
title: strings.select(),
|
||||
action: async () => {
|
||||
const verified =
|
||||
useSettingStore.getState().encryptBackups ||
|
||||
(await verifyAccount());
|
||||
if (!verified) return;
|
||||
|
||||
const backupStorageLocation =
|
||||
useSettingStore.getState().backupStorageLocation ||
|
||||
PATHS.backupsDirectory;
|
||||
const location = await desktop?.integration.selectDirectory.query(
|
||||
{
|
||||
title: strings.selectBackupDir(),
|
||||
defaultPath: backupStorageLocation
|
||||
}
|
||||
);
|
||||
if (!location) return;
|
||||
useSettingStore.getState().setBackupStorageLocation(location);
|
||||
},
|
||||
variant: "secondary"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -130,9 +130,9 @@ export const BehaviourSettings: SettingsGroup[] = [
|
||||
useSettingStore.getState().trashCleanupInterval.toString(),
|
||||
options: [
|
||||
{ value: "1", title: strings.daily() },
|
||||
{ value: "7", title: `7 ${strings.days()}` },
|
||||
{ value: "30", title: `30 ${strings.days()}` },
|
||||
{ value: "365", title: `365 ${strings.days()}` },
|
||||
{ value: "7", title: strings.days(7) },
|
||||
{ value: "30", title: strings.days(30) },
|
||||
{ value: "365", title: strings.days(365) },
|
||||
{ value: "-1", title: strings.never(), premium: true }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -171,12 +171,10 @@ export function ServersConfiguration() {
|
||||
.then((r) => r.json() as Promise<VersionResponse>)
|
||||
.catch(() => undefined);
|
||||
if (!version)
|
||||
throw new Error(
|
||||
`${strings.couldNotConnectTo(server.title)} .`
|
||||
);
|
||||
throw new Error(strings.couldNotConnectTo(server.title));
|
||||
if (version.id !== server.id)
|
||||
throw new Error(
|
||||
`${strings.incorrectServerUrl(url, server.title)}.`
|
||||
strings.incorrectServerUrl(url, server.title)
|
||||
);
|
||||
if (!isServerCompatible(version.version))
|
||||
throw new Error(
|
||||
|
||||
@@ -137,7 +137,7 @@ function ThemesList() {
|
||||
if (e instanceof Error)
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.failedToInstallTheme()}. Error: ` + e.message
|
||||
`${strings.failedToInstallTheme()} ${strings.error()}: ` + e.message
|
||||
);
|
||||
} finally {
|
||||
setIsApplying(false);
|
||||
|
||||
@@ -45,7 +45,7 @@ class TrashStore extends BaseStore<TrashStore> {
|
||||
restore = async (...ids: string[]) => {
|
||||
const restored = await db.trash.restore(...ids);
|
||||
if (restored === false) return;
|
||||
showToast("success", strings.action("item", ids.length, "restored"));
|
||||
showToast("success", strings.actions.restored.item(ids.length));
|
||||
await this.get().refresh();
|
||||
await appStore.refreshNavItems();
|
||||
await noteStore.refresh();
|
||||
|
||||
@@ -137,10 +137,7 @@ function useAuthenticateUser({
|
||||
const user = await db.user.fetchUser();
|
||||
setUser(user);
|
||||
} catch (e) {
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.biometricsAuthFailed()}. ${strings.pleaseTryAgain()}.`
|
||||
);
|
||||
showToast("error", strings.biometricsAuthFailed());
|
||||
openURL("/");
|
||||
} finally {
|
||||
setIsAuthenticating(false);
|
||||
@@ -325,7 +322,7 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
||||
title={strings.accountRecovery()}
|
||||
subtitle={strings.accountRecoveryWithKey()}
|
||||
loading={{
|
||||
title: `${strings.network.downloading()} (${progress})`,
|
||||
title: strings.network.downloading(progress),
|
||||
subtitle: strings.keyRecoveryProgressDesc()
|
||||
}}
|
||||
onSubmit={async (form) => {
|
||||
@@ -461,7 +458,7 @@ function NewPassword(props: BaseRecoveryComponentProps<"new">) {
|
||||
title={strings.resetAccountPassword()}
|
||||
subtitle={strings.accountPassDesc()}
|
||||
loading={{
|
||||
title: `${strings.resettingAccountPassword()} (${progress})`,
|
||||
title: strings.resettingAccountPassword(progress),
|
||||
subtitle: strings.resetPasswordWait()
|
||||
}}
|
||||
onSubmit={async (form) => {
|
||||
|
||||
@@ -61,7 +61,9 @@ function Trash() {
|
||||
if (e instanceof Error)
|
||||
showToast(
|
||||
"error",
|
||||
`${strings.couldNotClearTrash()}. Error: ${e.message}`
|
||||
`${strings.couldNotClearTrash()} ${strings.error()}: ${
|
||||
e.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +646,7 @@ const Tiptap = ({
|
||||
/>
|
||||
|
||||
<button
|
||||
className="unlock-note"
|
||||
style={{
|
||||
backgroundColor: colors.primary.accent,
|
||||
borderRadius: 5,
|
||||
|
||||
@@ -38,20 +38,20 @@ function StatusBar({
|
||||
const stickyRef = useRef(false);
|
||||
const prevScroll = useRef(0);
|
||||
const lastStickyChangeTime = useRef(0);
|
||||
const [words, setWords] = useState(`0 ${strings.words()}`);
|
||||
const [words, setWords] = useState(strings.totalWords(0));
|
||||
const currentWords = useRef(words);
|
||||
const statusBar = useRef({
|
||||
set: setStatus,
|
||||
updateWords: () => {
|
||||
const editor = editors[tab.id];
|
||||
if (!editor) return;
|
||||
const words = getTotalWords(editor as Editor) + ` ${strings.words()}`;
|
||||
const words = strings.totalWords(getTotalWords(editor as Editor));
|
||||
if (currentWords.current === words) return;
|
||||
setWords(words);
|
||||
},
|
||||
resetWords: () => {
|
||||
currentWords.current = `0 ${strings.words()}`;
|
||||
setWords(`0 ${strings.words()}`);
|
||||
currentWords.current = strings.totalWords(0);
|
||||
setWords(currentWords.current);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -31,8 +31,11 @@ import "./index.css";
|
||||
if (globalThis.__DEV__) {
|
||||
const logFn = global.console.log;
|
||||
global.console.log = function () {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
logFn.apply(console, arguments);
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
globalThis.logger("info", ...arguments);
|
||||
};
|
||||
}
|
||||
@@ -52,7 +55,7 @@ function loadApp() {
|
||||
})
|
||||
);
|
||||
|
||||
locale.then((locale) => {
|
||||
locale.then((locale: { [name: string]: any }) => {
|
||||
i18n.load(locale);
|
||||
i18n.activate(globalThis.LINGUI_LOCALE || "en");
|
||||
setI18nGlobal(i18n);
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"jsx": "react-jsx",
|
||||
"noEmit": true,
|
||||
"allowJs": true
|
||||
"allowJs": true,
|
||||
"moduleResolution": "nodenext",
|
||||
"module": "nodenext"
|
||||
},
|
||||
"include": ["src/"]
|
||||
}
|
||||
|
||||
3
packages/intl/.gitignore
vendored
3
packages/intl/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.swc
|
||||
locales
|
||||
locales
|
||||
generated/*.ts
|
||||
@@ -28,7 +28,7 @@ module.exports = {
|
||||
catalogs: [
|
||||
{
|
||||
path: "<rootDir>/locale/{locale}",
|
||||
include: ["src"]
|
||||
include: ["src", "generated"]
|
||||
}
|
||||
],
|
||||
format: "po",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
542
packages/intl/package-lock.json
generated
542
packages/intl/package-lock.json
generated
@@ -15,7 +15,9 @@
|
||||
"@lingui/swc-plugin": "^4.0.10",
|
||||
"@types/react": "^18.2.39",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"nodemon": "^3.1.7",
|
||||
"react": "18.2.0",
|
||||
"rollup": "^4.24.4",
|
||||
"typescript": "5.5.3",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-dts": "^4.2.3",
|
||||
@@ -877,9 +879,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/babel-plugin-extract-messages": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.11.4.tgz",
|
||||
"integrity": "sha512-7gUOsYJ4wIjv/0tGxAGiGpgWKCybFPP0tCQMz6baa9xcsk8Vp7Xmuf9og1AD6EMawjStibQsQyE6xaRnJgpoHg==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.14.0.tgz",
|
||||
"integrity": "sha512-lLabYkUsiF0un4ezColFm9Yf23c+efPR/5DRM9cFwW9uZJ5ajN4Ic00dBxJK9HOpy6fpaANnaHFmrm4NScijvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -887,22 +889,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-4.11.4.tgz",
|
||||
"integrity": "sha512-PauBkvi++YkYAYq6w9MwkBmE6KiDE9wRh5DkN8yFPRcfj64vkE2l1HFENCqL/jg63kr8esOAiueD9+CtUGVyDg==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-4.14.0.tgz",
|
||||
"integrity": "sha512-g9aJ76wpl6Fj06SEGiGnilAErEuZG6/woAU1HM81E0ZZ8ib1DF+cf81PJE+8Lwoxc0yMqY8qMScwhwcUhnR0aA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.0",
|
||||
"@babel/generator": "^7.21.1",
|
||||
"@babel/parser": "^7.21.2",
|
||||
"@babel/parser": "^7.22.0",
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@babel/types": "^7.21.2",
|
||||
"@lingui/babel-plugin-extract-messages": "4.11.4",
|
||||
"@lingui/conf": "4.11.4",
|
||||
"@lingui/core": "4.11.4",
|
||||
"@lingui/format-po": "4.11.4",
|
||||
"@lingui/message-utils": "4.11.4",
|
||||
"@lingui/babel-plugin-extract-messages": "4.14.0",
|
||||
"@lingui/conf": "4.14.0",
|
||||
"@lingui/core": "4.14.0",
|
||||
"@lingui/format-po": "4.14.0",
|
||||
"@lingui/message-utils": "4.14.0",
|
||||
"babel-plugin-macros": "^3.0.1",
|
||||
"chalk": "^4.1.0",
|
||||
"chokidar": "3.5.1",
|
||||
@@ -930,6 +932,53 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/@lingui/conf": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.14.0.tgz",
|
||||
"integrity": "sha512-wHZ4s79wqPnROa3xEoUTNep7sjqwbDHCMLBhsHbcvk/3ACDKgm0TMvDnIRjMRwrPRsK98yxSmWaE+NKhT0Ggyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^8.0.0",
|
||||
"jest-validate": "^29.4.3",
|
||||
"jiti": "^1.17.1",
|
||||
"lodash.get": "^4.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/@lingui/core": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.14.0.tgz",
|
||||
"integrity": "sha512-rsuBKG3mupWpfP5RYacufu0ToavrqgFPnEI/zOcuvMNL24IlZ38O2/P3POZCEA8XCyhON70FICy8xs3ZvQ+uWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@lingui/message-utils": "4.14.0",
|
||||
"unraw": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/@lingui/message-utils": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.14.0.tgz",
|
||||
"integrity": "sha512-izZod/6P2cXtqZt06r+VcbG3oCskPY8A1dVA8hyx0UZBCaufrZk6NgzCMYTMRXyOuXt6MafQyUfBJSrE4n3FIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@messageformat/parser": "^5.0.0",
|
||||
"js-sha256": "^0.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/cli/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
@@ -1110,14 +1159,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-4.11.4.tgz",
|
||||
"integrity": "sha512-PiWbTiiNgYZTFVuBHYirtAG98cDxrT0IwmSvETQk4YbaqCdn28/J7fRFZScsKqe8LmpnxX4EXZLs/R0MP2jLHA==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-4.14.0.tgz",
|
||||
"integrity": "sha512-3Bmk9f7lRuBoZsBG5c5hq5ipgAqeux+nDVnAIXSfsp4f8jj+z+cHwW0xuHcW48mHQFwfcTGAYZcKnwhuwwut6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lingui/conf": "4.11.4",
|
||||
"@lingui/message-utils": "4.11.4",
|
||||
"@lingui/conf": "4.14.0",
|
||||
"@lingui/message-utils": "4.14.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"pofile": "^1.1.4"
|
||||
},
|
||||
@@ -1125,6 +1174,114 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/@lingui/conf": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.14.0.tgz",
|
||||
"integrity": "sha512-wHZ4s79wqPnROa3xEoUTNep7sjqwbDHCMLBhsHbcvk/3ACDKgm0TMvDnIRjMRwrPRsK98yxSmWaE+NKhT0Ggyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^8.0.0",
|
||||
"jest-validate": "^29.4.3",
|
||||
"jiti": "^1.17.1",
|
||||
"lodash.get": "^4.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/@lingui/message-utils": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.14.0.tgz",
|
||||
"integrity": "sha512-izZod/6P2cXtqZt06r+VcbG3oCskPY8A1dVA8hyx0UZBCaufrZk6NgzCMYTMRXyOuXt6MafQyUfBJSrE4n3FIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@messageformat/parser": "^5.0.0",
|
||||
"js-sha256": "^0.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/format-po/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/macro": {
|
||||
"version": "4.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-4.11.4.tgz",
|
||||
@@ -1160,6 +1317,55 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/react": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.13.0.tgz",
|
||||
"integrity": "sha512-5remR9rVwosiiX/RnEWETHA8cpqQiP7U87OXXMPz67LuyG3XP8RP+ic75rVn284DHLHgpjDbauz7vYIz855ZoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@lingui/core": "4.13.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/react/node_modules/@lingui/core": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.13.0.tgz",
|
||||
"integrity": "sha512-kRqQWeEVoqNrDtEdyHPWGsAHRStN8ObYc5a1gdyuBhoj1zaoUS/DMK5C7B1ZeTtj6rCCmZRs6d2tN12hsZ2zJA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@lingui/message-utils": "4.13.0",
|
||||
"unraw": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/react/node_modules/@lingui/message-utils": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.13.0.tgz",
|
||||
"integrity": "sha512-tI/WBVZym+APwqk0O3xTaF0k+RQIv5E4PqGHdXqwbofycHly2C+izH+hg6UeNctc6jd19GRwqu/4ga9knkdAlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@messageformat/parser": "^5.0.0",
|
||||
"js-sha256": "^0.10.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lingui/swc-plugin": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@lingui/swc-plugin/-/swc-plugin-4.0.10.tgz",
|
||||
@@ -1380,9 +1586,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz",
|
||||
"integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1394,9 +1600,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz",
|
||||
"integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1408,9 +1614,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
|
||||
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz",
|
||||
"integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1422,9 +1628,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
|
||||
"integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz",
|
||||
"integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1435,10 +1641,38 @@
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz",
|
||||
"integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz",
|
||||
"integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz",
|
||||
"integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1450,9 +1684,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
|
||||
"integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz",
|
||||
"integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1464,9 +1698,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz",
|
||||
"integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1478,9 +1712,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz",
|
||||
"integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1492,9 +1726,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz",
|
||||
"integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1506,9 +1740,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz",
|
||||
"integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1520,9 +1754,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz",
|
||||
"integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -1534,9 +1768,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
|
||||
"integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz",
|
||||
"integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1548,9 +1782,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
|
||||
"integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz",
|
||||
"integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1562,9 +1796,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz",
|
||||
"integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1576,9 +1810,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz",
|
||||
"integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1590,9 +1824,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
|
||||
"integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz",
|
||||
"integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2330,6 +2564,7 @@
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
@@ -3117,6 +3352,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore-by-default": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -3840,6 +4082,86 @@
|
||||
"integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
|
||||
"integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -4209,6 +4531,13 @@
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -4357,9 +4686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
|
||||
"integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz",
|
||||
"integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4373,22 +4702,24 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.24.0",
|
||||
"@rollup/rollup-android-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.24.0",
|
||||
"@rollup/rollup-darwin-x64": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.24.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.24.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.24.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.24.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.24.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.24.4",
|
||||
"@rollup/rollup-android-arm64": "4.24.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.24.4",
|
||||
"@rollup/rollup-darwin-x64": "4.24.4",
|
||||
"@rollup/rollup-freebsd-arm64": "4.24.4",
|
||||
"@rollup/rollup-freebsd-x64": "4.24.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.24.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.24.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.24.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.24.4",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.24.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.24.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.24.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.24.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.24.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.24.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.24.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.24.4",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -4478,6 +4809,32 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.8.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
|
||||
@@ -4628,6 +4985,16 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
|
||||
@@ -4675,6 +5042,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undefsafe": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
"url": "git://github.com/streetwriters/notesnook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"extract": "lingui extract",
|
||||
"extract": "lingui extract --clean --overwrite",
|
||||
"compile": "lingui compile --typescript",
|
||||
"build-locale": "npm run extract && npm run compile",
|
||||
"build-locale": "node scripts/generate-strings.mjs && npm run extract && npm run compile",
|
||||
"build": "npm run build-locale && npx vite build && node scripts/postbuild.mjs",
|
||||
"watch": "npx vite build --watch",
|
||||
"watch": "nodemon --watch scripts/ --watch src --ext ts,mjs --exec \"npm run build\"",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -36,7 +36,9 @@
|
||||
"@lingui/swc-plugin": "^4.0.10",
|
||||
"@types/react": "^18.2.39",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"nodemon": "^3.1.7",
|
||||
"react": "18.2.0",
|
||||
"rollup": "^4.24.4",
|
||||
"typescript": "5.5.3",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-dts": "^4.2.3",
|
||||
|
||||
315
packages/intl/scripts/generate-strings.mjs
Normal file
315
packages/intl/scripts/generate-strings.mjs
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
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 { mkdirSync, writeFileSync } from "fs";
|
||||
|
||||
const DO_ACTIONS = [
|
||||
{
|
||||
action: "delete",
|
||||
label: "Delete",
|
||||
dataTypes: [
|
||||
"note",
|
||||
"item",
|
||||
"reminder",
|
||||
"notebook",
|
||||
"tag",
|
||||
"color",
|
||||
"attachment"
|
||||
]
|
||||
},
|
||||
{ action: "unpin", label: "Unpin", dataTypes: ["note", "notebook"] },
|
||||
{ action: "pin", label: "Pin", dataTypes: ["note", "notebook"] },
|
||||
{ action: "unpublish", label: "Unpublish", dataTypes: ["note"] },
|
||||
{ action: "publish", label: "Publish", dataTypes: ["note"] },
|
||||
{
|
||||
action: "permanentlyDelete",
|
||||
label: "Permanently delete",
|
||||
dataTypes: ["attachment", "item"]
|
||||
},
|
||||
{
|
||||
action: "restore",
|
||||
label: "Restore",
|
||||
dataTypes: ["note", "notebook"]
|
||||
},
|
||||
{
|
||||
action: "edit",
|
||||
label: "Edit",
|
||||
dataTypes: ["tag", "notebook", "reminder"]
|
||||
},
|
||||
{
|
||||
action: "rename",
|
||||
label: "Rename",
|
||||
dataTypes: ["attachment", "color", "tag"]
|
||||
},
|
||||
{
|
||||
action: "remove",
|
||||
label: "Remove",
|
||||
dataTypes: ["shortcut", "color", "attachment"]
|
||||
},
|
||||
{
|
||||
action: "download",
|
||||
label: "Download",
|
||||
dataTypes: ["attachment"]
|
||||
}
|
||||
];
|
||||
|
||||
const ACTIONS = [
|
||||
{
|
||||
action: "deleted",
|
||||
label: "deleted",
|
||||
dataTypes: ["attachment", "reminder", "tag", "note"]
|
||||
},
|
||||
{
|
||||
action: "movedToTrash",
|
||||
label: "moved to trash",
|
||||
dataTypes: ["note", "notebook"]
|
||||
},
|
||||
{
|
||||
action: "permanentlyDeleted",
|
||||
label: "permanently deleted",
|
||||
dataTypes: ["note", "notebook"]
|
||||
},
|
||||
{ action: "published", label: "published", dataTypes: ["note"] },
|
||||
{
|
||||
action: "restored",
|
||||
label: "restored",
|
||||
dataTypes: ["note", "notebook", "item"]
|
||||
},
|
||||
{
|
||||
action: "edited",
|
||||
label: "edited",
|
||||
dataTypes: ["tag", "notebook"]
|
||||
},
|
||||
{
|
||||
action: "created",
|
||||
label: "created",
|
||||
dataTypes: ["notebook", "tag", "shortcut"]
|
||||
},
|
||||
{
|
||||
action: "renamed",
|
||||
label: "renamed",
|
||||
dataTypes: ["color"]
|
||||
}
|
||||
];
|
||||
|
||||
const ACTION_CONFIRMATIONS = [
|
||||
{
|
||||
action: "delete",
|
||||
label: "delete",
|
||||
dataTypes: [
|
||||
"note",
|
||||
"item",
|
||||
"reminder",
|
||||
"notebook",
|
||||
"tag",
|
||||
"color",
|
||||
"attachment"
|
||||
]
|
||||
},
|
||||
{
|
||||
action: "permanentlyDelete",
|
||||
label: "permanently delete",
|
||||
dataTypes: ["note", "notebook"]
|
||||
}
|
||||
];
|
||||
|
||||
const ACTION_ERRORS = [
|
||||
{
|
||||
action: "unpublished",
|
||||
label: "unpublished",
|
||||
dataTypes: ["note"]
|
||||
},
|
||||
{
|
||||
action: "published",
|
||||
label: "published",
|
||||
dataTypes: ["note"]
|
||||
}
|
||||
];
|
||||
|
||||
const IN_PROGRESS_ACTIONS = [
|
||||
{
|
||||
action: "deleting",
|
||||
label: "Deleting",
|
||||
dataTypes: ["note", "notebook", "attachment", "tag", "reminder"]
|
||||
}
|
||||
];
|
||||
|
||||
const DATA_TYPES = {
|
||||
note: {
|
||||
singular: `note`,
|
||||
singularCamelCase: `Note`,
|
||||
plural: `notes`,
|
||||
pluralCamelCase: `Notes`
|
||||
},
|
||||
notebook: {
|
||||
singular: `notebook`,
|
||||
singularCamelCase: `Notebook`,
|
||||
plural: `notebooks`,
|
||||
pluralCamelCase: `Notebooks`
|
||||
},
|
||||
tag: {
|
||||
singular: `tag`,
|
||||
singularCamelCase: `Tag`,
|
||||
plural: `tags`,
|
||||
pluralCamelCase: `Tags`
|
||||
},
|
||||
reminder: {
|
||||
singular: `reminder`,
|
||||
singularCamelCase: `Reminder`,
|
||||
plural: `reminders`,
|
||||
pluralCamelCase: `Reminders`
|
||||
},
|
||||
color: {
|
||||
singular: `color`,
|
||||
singularCamelCase: `Color`,
|
||||
plural: `colors`,
|
||||
pluralCamelCase: `Colors`
|
||||
},
|
||||
attachment: {
|
||||
singular: `attachment`,
|
||||
singularCamelCase: `Attachment`,
|
||||
plural: `attachments`,
|
||||
pluralCamelCase: `Attachments`
|
||||
},
|
||||
item: {
|
||||
singular: `item`,
|
||||
singularCamelCase: `Item`,
|
||||
plural: `items`,
|
||||
pluralCamelCase: `Items`
|
||||
},
|
||||
shortcut: {
|
||||
singular: `shortcut`,
|
||||
singularCamelCase: `Shortcut`,
|
||||
plural: `shortcuts`,
|
||||
pluralCamelCase: `Shortcuts`
|
||||
}
|
||||
};
|
||||
|
||||
const DO_ACTIONS_TEMPLATE = (action, dataTypes) => `${action}: {
|
||||
${dataTypes}
|
||||
}`;
|
||||
|
||||
const DATA_TYPES_TEMPLATE = (
|
||||
type,
|
||||
singularTemplate,
|
||||
pluralTemplate
|
||||
) => `${type}: (count: number) => plural(count, {
|
||||
one: \`${singularTemplate}\`,
|
||||
other: \`${pluralTemplate}\`
|
||||
})`;
|
||||
|
||||
const MODULE_TEMPLATE = (exportName, strings) =>
|
||||
`/* eslint-disable header/header */
|
||||
// THIS FILE IS GENERATED. DO NOT EDIT MANUALLY.
|
||||
|
||||
import { t, plural } from "@lingui/macro";
|
||||
|
||||
export const ${exportName} = {
|
||||
${strings}
|
||||
};
|
||||
`;
|
||||
|
||||
function generateDoActionsStrings() {
|
||||
return generateStrings(
|
||||
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}`
|
||||
);
|
||||
}
|
||||
|
||||
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}?`
|
||||
);
|
||||
}
|
||||
|
||||
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}`
|
||||
);
|
||||
}
|
||||
|
||||
function generateInProgressActionsStrings() {
|
||||
return generateStrings(
|
||||
IN_PROGRESS_ACTIONS,
|
||||
(action, type) => `${action} ${DATA_TYPES[type].singular}...`,
|
||||
(action, type) => `${action} # ${DATA_TYPES[type].plural}...`
|
||||
);
|
||||
}
|
||||
|
||||
function generateStrings(actions, singular, plural) {
|
||||
let result = [];
|
||||
for (const action of actions) {
|
||||
const subResults = [];
|
||||
for (const type of action.dataTypes) {
|
||||
const actionName = action.label;
|
||||
subResults.push(
|
||||
DATA_TYPES_TEMPLATE(
|
||||
type,
|
||||
singular(actionName, type),
|
||||
plural(actionName, type)
|
||||
)
|
||||
);
|
||||
}
|
||||
result.push(DO_ACTIONS_TEMPLATE(action.action, subResults.join(",\n")));
|
||||
}
|
||||
return result.join(",\n");
|
||||
}
|
||||
|
||||
mkdirSync("./generated/", { recursive: true });
|
||||
|
||||
writeFileSync(
|
||||
"./generated/do-actions.ts",
|
||||
MODULE_TEMPLATE("doActions", generateDoActionsStrings())
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
"./generated/actions.ts",
|
||||
MODULE_TEMPLATE("actions", generateActionsStrings())
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
"./generated/in-progress-actions.ts",
|
||||
MODULE_TEMPLATE("inProgressActions", generateInProgressActionsStrings())
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
"./generated/action-errors.ts",
|
||||
MODULE_TEMPLATE("actionErrors", generateActionErrorStrings())
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
"./generated/action-confirmations.ts",
|
||||
MODULE_TEMPLATE("actionConfirmations", generateActionConfirmationStrings())
|
||||
);
|
||||
@@ -17,44 +17,27 @@ 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 { fail } from "assert";
|
||||
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";
|
||||
|
||||
const actions = {
|
||||
deleted: () => t`deleted`,
|
||||
unpinned: () => t`unpinned`,
|
||||
pinned: () => t`pinned`,
|
||||
unpublished: () => t`unpublished`,
|
||||
published: () => t`published`,
|
||||
permanentlyDeleted: () => t`permanently deleted`,
|
||||
restored: () => t`restored`,
|
||||
edited: () => t`edited`,
|
||||
created: () => t`created`,
|
||||
renamed: () => t`renamed`
|
||||
const SEARCH_IN_ROUTE_STRINGS = {
|
||||
Notes: () => t`Search in in Notes`,
|
||||
Notebooks: () => t`Search in in Notebooks`,
|
||||
Notebook: () => t`Search in in Notebook`,
|
||||
Favorites: () => t`Search in in Favorites`,
|
||||
Reminders: () => t`Search in in Reminders`,
|
||||
Trash: () => t`Search in in Trash`,
|
||||
Settings: () => t`Search in in Settings`,
|
||||
Tags: () => t`Search in in Tags`,
|
||||
Editor: () => t`Search in in Editor`,
|
||||
Home: () => t`Search in in Home`,
|
||||
Search: () => t`Search in in Search`,
|
||||
Monographs: () => t`Search in in Monographs`
|
||||
};
|
||||
|
||||
const doActions = {
|
||||
delete: () => t`Delete`,
|
||||
unpin: () => t`Unpin`,
|
||||
pin: () => t`Pin`,
|
||||
unpublish: () => t`Unpublish`,
|
||||
publish: () => t`Publish`,
|
||||
permanentlyDelete: () => t`Permanently delete`,
|
||||
restore: () => t`Restore`,
|
||||
edit: () => t`Edit`,
|
||||
create: () => t`Created`,
|
||||
rename: () => t`Rename`,
|
||||
remove: () => t`Remove`,
|
||||
download: () => t`Download`
|
||||
};
|
||||
|
||||
const inProgressActions = {
|
||||
deleting: () => t`Delete`
|
||||
};
|
||||
|
||||
type Actions = keyof typeof actions;
|
||||
type DoActions = keyof typeof doActions;
|
||||
type InProgressActions = keyof typeof inProgressActions;
|
||||
|
||||
export const strings = {
|
||||
done: () => t`Done`,
|
||||
verifyItsYou: () => t`Please verify it's you`,
|
||||
@@ -67,10 +50,11 @@ export const strings = {
|
||||
}),
|
||||
downloading: () => t`Downloading`,
|
||||
uploading: () => t`Uploading`,
|
||||
networkProgress: (type: "upload" | "download") =>
|
||||
networkProgress: (type: "upload" | "download" | "sync") =>
|
||||
select(type, {
|
||||
upload: "Uploading",
|
||||
download: "Downloading",
|
||||
sync: "Syncing",
|
||||
other: "Loading"
|
||||
}),
|
||||
tapToCancel: () => t`Tap to cancel`,
|
||||
@@ -326,10 +310,14 @@ export const strings = {
|
||||
item: () => t`Items`,
|
||||
shortcut: () => t`Shortcuts`
|
||||
},
|
||||
addItem: (referenceType: string) =>
|
||||
t`Add a ${strings.dataTypes[
|
||||
referenceType as keyof typeof strings.dataTypes
|
||||
]()}`,
|
||||
addItem: (itemType: "tag" | "notebook" | "reminder" | "note") =>
|
||||
select(itemType, {
|
||||
tag: `Add a tag`,
|
||||
notebook: `Add a notebook`,
|
||||
reminder: `Add a reminder`,
|
||||
note: `Add a note`,
|
||||
other: `Add an item`
|
||||
}),
|
||||
reminderRepeatStrings: {
|
||||
day: (date: string) => t`Repeats daily at ${date}`,
|
||||
week: {
|
||||
@@ -340,21 +328,32 @@ export const strings = {
|
||||
month: {
|
||||
selectDays: () => t`Select nth day of the month to repeat the reminder.`
|
||||
},
|
||||
repeats: (freq: number, mode: string, selectedDays: string, date: string) =>
|
||||
plural(freq, {
|
||||
one: `Repeats every ${strings.reminderRepeatMode[
|
||||
mode as keyof typeof strings.reminderRepeatMode
|
||||
]()} on ${selectedDays} at ${date}`,
|
||||
other: `Repeats every ${freq} ${strings.reminderRepeatMode[
|
||||
mode as keyof typeof strings.reminderRepeatMode
|
||||
]()} every ${selectedDays} at ${date}`
|
||||
})
|
||||
},
|
||||
reminderRepeatMode: {
|
||||
day: () => t`day`,
|
||||
week: () => t`week`,
|
||||
month: () => t`month`,
|
||||
year: () => t`year`
|
||||
repeats: (
|
||||
freq: number,
|
||||
mode: string,
|
||||
selectedDays: string,
|
||||
date: string
|
||||
) => {
|
||||
const strings = {
|
||||
day: plural(freq, {
|
||||
one: `Repeats every day on ${selectedDays} at ${date}`,
|
||||
other: `Repeats every # day every ${selectedDays} at ${date}`
|
||||
}),
|
||||
week: plural(freq, {
|
||||
one: `Repeats every week on ${selectedDays} at ${date}`,
|
||||
other: `Repeats every # week every ${selectedDays} at ${date}`
|
||||
}),
|
||||
month: plural(freq, {
|
||||
one: `Repeats every month on ${selectedDays} at ${date}`,
|
||||
other: `Repeats every # month every ${selectedDays} at ${date}`
|
||||
}),
|
||||
year: plural(freq, {
|
||||
one: `Repeats every year on ${selectedDays} at ${date}`,
|
||||
other: `Repeats every # year every ${selectedDays} at ${date}`
|
||||
})
|
||||
};
|
||||
return strings[mode as keyof typeof strings];
|
||||
}
|
||||
},
|
||||
remindMeIn: () => t`Remind me in`,
|
||||
referencedIn: () => t`REFERENCED IN`,
|
||||
@@ -362,13 +361,13 @@ export const strings = {
|
||||
t`Select the folder that includes your backup files to list them here.`,
|
||||
noBackupsFound: () => t`No backups found`,
|
||||
restoring: () => t`Restoring`,
|
||||
restoringCollection: (collection: string) => t`Restoring ${collection}...`,
|
||||
checkNewVersion: () => t`Checking for new version`,
|
||||
noUpdates: () => t`No updates available`,
|
||||
updateAvailable: () => t`Update available`,
|
||||
versionReleased: (version: string, type: "github" | "store") =>
|
||||
select(type, {
|
||||
github: `v${version} has been released on GitHub`,
|
||||
store: `v${version} has been released`,
|
||||
other: `v${version} has been released`
|
||||
}),
|
||||
readReleaseNotes: () => t`Read full release notes on Github`,
|
||||
@@ -387,7 +386,7 @@ export const strings = {
|
||||
gettingRecoveryCodes: () => t`Getting recovery codes`,
|
||||
protectNotes: () => t`Protect your notes`,
|
||||
protectNotesDesc: () => t`Choose how you want to secure your notes locally.`,
|
||||
loggingOut: () => t`Logging out`,
|
||||
loggingOut: () => t`Logging out. Please wait...`,
|
||||
loggingOutDesc: () => t`Please wait while we log you out.`,
|
||||
by: () => t`By`,
|
||||
noResultsForSearch: (query: string) => t`No results found for "${query}"`,
|
||||
@@ -425,13 +424,12 @@ $headline$: Use starting line of the note as title.`,
|
||||
other: ""
|
||||
}),
|
||||
|
||||
createVault: () => t`Create Vault`,
|
||||
createVaultDesc: () =>
|
||||
t`A vault stores your notes in a password-encrypted storage.`,
|
||||
vaultFingerprintUnlock: () => t`Vault Fingerprint Unlock`,
|
||||
revokeVaultFingerprintUnlock: () => t`Revoke Vault Fingerprint Unlock`,
|
||||
changeVaultPassword: () => t`Change Vault Password`,
|
||||
deleteNote: () => t`Delete note`,
|
||||
createVault: () => t`Create vault`,
|
||||
createVaultDesc: () => t`A vault stores your notes in a encrypted storage.`,
|
||||
vaultFingerprintUnlock: () => t`Vault fingerprint unlock`,
|
||||
revokeVaultFingerprintUnlock: () => t`Revoke vault fingerprint unlock`,
|
||||
changeVaultPassword: () => t`Change vault password`,
|
||||
deleteNote: () => doActions.delete.note(1),
|
||||
shareNote: () => t`Share note`,
|
||||
copyNote: () => t`Copy note`,
|
||||
goToEditor: () => t`Unlock note`,
|
||||
@@ -448,6 +446,8 @@ $headline$: Use starting line of the note as title.`,
|
||||
t`Your account email will be changed without affecting your subscription or any other settings.`,
|
||||
changeEmailNotice: () => t`You will be logged out from all your devices`,
|
||||
export: () => t`Export`,
|
||||
exportNotes: (notes: number) =>
|
||||
plural(notes, { one: "Export note", other: "Export # notes" }),
|
||||
issueTitle: () => t`Report issue`,
|
||||
issueDesc: () =>
|
||||
t`We are sorry, it seems that the app crashed due to an error. You can submit a bug report below so we can fix this asap.`,
|
||||
@@ -463,7 +463,8 @@ $headline$: Use starting line of the note as title.`,
|
||||
}),
|
||||
addNotesToNotebook: (title: string) => t`Add notes to ${title}`,
|
||||
publish: () => t`Publish`,
|
||||
publishDesc: () =>
|
||||
publishNote: () => t`Publish note`,
|
||||
publishNoteDesc: () =>
|
||||
t`Publish your note to share it with others. You can set a password to protect it.`,
|
||||
saveRecoveryKey: () => t`Save account recovery key`,
|
||||
saveRecoveryKeyDesc: () =>
|
||||
@@ -481,7 +482,8 @@ $headline$: Use starting line of the note as title.`,
|
||||
twoFactorAuthEnabled: () => t`Two-factor authentication enabled`,
|
||||
listOf: () => t`List of`,
|
||||
network: {
|
||||
downloading: () => t`Downloading`,
|
||||
downloading: (progress?: string | number) =>
|
||||
progress ? t`Downloading (${progress})` : t`Downloading`,
|
||||
downloaded: () => t`Downloaded`,
|
||||
download: () => t`Download`,
|
||||
upload: () => t`Upload`,
|
||||
@@ -498,7 +500,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
redo: () => t`Redo`,
|
||||
createYourAccount: () => t`Create your account`,
|
||||
pinned: () => t`Pinned`,
|
||||
editNotebook: () => t`Edit notebook`,
|
||||
editNotebook: () => doActions.edit.notebook(1),
|
||||
newNotebook: () => t`New notebook`,
|
||||
newInternalLink: () => t`Link to note`,
|
||||
editInternalLink: () => t`Edit internal link`,
|
||||
@@ -506,7 +508,9 @@ $headline$: Use starting line of the note as title.`,
|
||||
tabs: () => t`Tabs`,
|
||||
add: () => t`Add`,
|
||||
newVersion: () => t`New version`,
|
||||
editReminder: () => t`Edit reminder`,
|
||||
newVersionHighlights: (version?: string) =>
|
||||
t`${version ? `v${version} ` : "New version"} Highlights 🎉`,
|
||||
editReminder: () => doActions.edit.reminder(1),
|
||||
newReminder: () => t`New reminder`,
|
||||
sortBy: () => t`Sort by`,
|
||||
groupBy: () => t`Group by`,
|
||||
@@ -542,7 +546,6 @@ $headline$: Use starting line of the note as title.`,
|
||||
keep: () => t`Keep`,
|
||||
restore: () => t`Restore`,
|
||||
deletePermanently: () => t`Delete permanently`,
|
||||
deletedPermanently: () => t`deleted permanently`,
|
||||
viewAllLinkedNotebooks: () => t`View all linked notebooks`,
|
||||
learnMore: () => t`Learn more`,
|
||||
addTag: () => t`Add tag`,
|
||||
@@ -635,7 +638,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
},
|
||||
downloadUpdate: () => t`Download update`,
|
||||
stopReordering: () => t`Tap to stop reordering`,
|
||||
removeShortcut: () => t`Remove shortcut`,
|
||||
removeShortcut: () => doActions.remove.shortcut(1),
|
||||
createShortcut: () => t`Create a shortcut`,
|
||||
tip: () => t`TIP`,
|
||||
neverShowAgain: () => t`Never show again`,
|
||||
@@ -749,7 +752,10 @@ $headline$: Use starting line of the note as title.`,
|
||||
enterPassword: () => t`Enter password`,
|
||||
failedToDownloadFile: () => t`Failed to download file`,
|
||||
zipping: () => t`Zipping`,
|
||||
savingZipFile: () => t`Saving zip file`,
|
||||
savingZipFile: (progress?: string) =>
|
||||
progress
|
||||
? t`Saving zip file (${progress}%). Please wait...`
|
||||
: t`Saving zip file. Please wait...`,
|
||||
failedToZipFiles: () => t`Failed to zip files`,
|
||||
fileVerificationFailed: () => t`Uploaded file verification failed.`,
|
||||
fileLengthError: () =>
|
||||
@@ -793,34 +799,15 @@ $headline$: Use starting line of the note as title.`,
|
||||
user: "@andrewsayer on Twitter"
|
||||
}
|
||||
],
|
||||
shortcutCreated: () => t`Shortcut created`,
|
||||
notebookRestored: () => t`Notebook restored`,
|
||||
restoreNotebook: () => t`Restore notebook`,
|
||||
permanentlyDeletedNotebook: () => t`Permanently deleted notebook`,
|
||||
shortcutCreated: () => actions.created.shortcut(1),
|
||||
notebookRestored: () => actions.restored.notebook(1),
|
||||
restoreNotebook: () => doActions.restore.notebook(1),
|
||||
permanentlyDeletedNotebook: () => actions.permanentlyDeleted.notebook(1),
|
||||
noteRestoredFromHistory: () => t`Note restored from history`,
|
||||
noteRestored: () => t`Note restored`,
|
||||
deleteNoteConfirmation: () =>
|
||||
t`Are you sure you want to delete this note permanently?`,
|
||||
noteDeleted: () => t`Note deleted`,
|
||||
noteRestored: () => actions.restored.note(1),
|
||||
deleteNoteConfirmation: () => actionConfirmations.permanentlyDelete.note(1),
|
||||
noteDeleted: () => actions.deleted.note(1),
|
||||
restored: () => t`Restored successfully`,
|
||||
deleteItems: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `Delete ${strings.dataTypes[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()}`,
|
||||
other: `Delete ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()}`
|
||||
}),
|
||||
deleteItemsConfirmation: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `Are you sure you want to delete this ${strings.dataTypes[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} permanently?`,
|
||||
other: `Are you sure you want to delete these ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} permanently?`
|
||||
}),
|
||||
manageTags: () => t`Manage tags`,
|
||||
linkNotebook: () => t`Link to notebook`,
|
||||
move: () => t`Move`,
|
||||
@@ -840,7 +827,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
}),
|
||||
failedToPublish: () => t`Failed to publish note`,
|
||||
failedToUnpublish: () => t`Failed to unpublish note`,
|
||||
notePublished: () => t`Note published`,
|
||||
notePublished: () => actions.published.note(1),
|
||||
monographUrlCopied: () => t`Monograph URL copied`,
|
||||
recoveryKeySaved: () => t`Did you save recovery key?`,
|
||||
recoveryKeySavedDesc: () =>
|
||||
@@ -855,22 +842,13 @@ $headline$: Use starting line of the note as title.`,
|
||||
},
|
||||
backupEncrypted: () => t`Backup is encrypted`,
|
||||
password: () => t`Password`,
|
||||
renameTag: () => t`Rename tag`,
|
||||
renameColor: () => t`Rename color`,
|
||||
renameTag: () => doActions.rename.tag(1),
|
||||
renameColor: () => doActions.rename.color(1),
|
||||
renameColorDesc: (color: string) => t`You are renaming color ${color}`,
|
||||
name: () => t`Name`,
|
||||
unlockToDelete: () => t`Unlock note to delete it`,
|
||||
backupRestored: () => t`Backup restored`,
|
||||
restoreFailed: () => t`Restore failed`,
|
||||
itemDeleted: (count: number, type: string) =>
|
||||
plural(count, {
|
||||
one: `1 ${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypesCamelCase
|
||||
]()} deleted`,
|
||||
other: `# ${strings.dataTypes[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} deleted`
|
||||
}),
|
||||
reorder: () => t`Reorder`,
|
||||
turnOffReminder: () => t`Turn off reminder`,
|
||||
turnOnReminder: () => t`Turn on reminder`,
|
||||
@@ -894,12 +872,15 @@ $headline$: Use starting line of the note as title.`,
|
||||
readOnly: () => t`Read only`,
|
||||
syncOff: () => t`Sync off`,
|
||||
syncOffConfirm: (count: number) =>
|
||||
t`Prevent ${strings.itemsPlural("note", count)} from syncing`,
|
||||
plural(count, {
|
||||
one: `Prevent note from syncing`,
|
||||
other: `Prevent # notes from syncing`
|
||||
}),
|
||||
syncOffDesc: (count: number) =>
|
||||
`${strings.itemsPlural(
|
||||
"note",
|
||||
count
|
||||
)} will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?`,
|
||||
plural(count, {
|
||||
one: `Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?`,
|
||||
other: `# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?`
|
||||
}),
|
||||
duplicate: () => t`Duplicate`,
|
||||
remindMe: () => t`Remind me`,
|
||||
published: () => t`Published`,
|
||||
@@ -908,7 +889,7 @@ $headline$: Use starting line of the note as title.`,
|
||||
linkNotebooks: () => t`Link notebooks`,
|
||||
unlinkFromAll: () => t`Unlink from all`,
|
||||
removeFromAll: () => t`Remove from all`,
|
||||
assignTo: () => t`Assign to`,
|
||||
assignTo: () => t`Assign to...`,
|
||||
addTags: () => t`Add tags`,
|
||||
addTagsDesc: () => t`Add tags to multiple notes at once`,
|
||||
references: () => t`References`,
|
||||
@@ -1190,10 +1171,10 @@ $headline$: Use starting line of the note as title.`,
|
||||
automaticBackupsDesc: () =>
|
||||
t`Set the interval to create a partial backup (without attachments) automatically.`,
|
||||
automaticBackupsWithAttachments: () => t`Automatic backups with attachments`,
|
||||
automaticBackupsWithAttachmentsDesc: () =>
|
||||
t`Set the interval to create a backup (with attachments) automatically.
|
||||
|
||||
NOTE: Creating a backup with attachments can take a while, and also fail completely. The app will try to resume/restart the backup in case of interruptions.`,
|
||||
automaticBackupsWithAttachmentsDesc: () => [
|
||||
t`Set the interval to create a backup (with attachments) automatically.`,
|
||||
t`NOTE: Creating a backup with attachments can take a while, and also fail completely. The app will try to resume/restart the backup in case of interruptions.`
|
||||
],
|
||||
selectBackupDir: () => t`Select backup directory`,
|
||||
selectBackupDirDesc: () => t`Choose where to save your backups`,
|
||||
noDirectorySelected: () => t`No directory selected`,
|
||||
@@ -1310,18 +1291,17 @@ NOTE: Creating a backup with attachments can take a while, and also fail complet
|
||||
backupDataDesc: () =>
|
||||
t`All your backups are stored in 'Phone Storage/Notesnook/backups/' folder`,
|
||||
backupSuccess: () => t`Backup successful`,
|
||||
biometricsAuthFailed: () => t`Biometrics authentication failed`,
|
||||
biometricsAuthFailed: () =>
|
||||
t`Biometrics authentication failed. Please try again.`,
|
||||
biometricsAuthFailedDesc: () => t`Wait 30 seconds to try again`,
|
||||
biometricsAuthCancelled: () => t`Authentication cancelled by user`,
|
||||
biometricsAuthError: () => t`Authentication failed`,
|
||||
tryAgain: () => t`Tap to try again`,
|
||||
rateAppMessage: () => t`We would love to know what you think!`,
|
||||
rateAppActionText: (platform: string) =>
|
||||
t`Rate Notesnook on ${
|
||||
platform === "ios" ? strings.appStore() : strings.playStore()
|
||||
}`,
|
||||
appStore: () => t`App Store`,
|
||||
playStore: () => t`Play Store`,
|
||||
platform === "ios"
|
||||
? t`Rate Notesnook on App Store`
|
||||
: t`Rate Notesnook on Play Store`,
|
||||
recoveryKeyMessage: () => t`Keep your data safe`,
|
||||
recoveryKeyMessageActionText: () => t`Save your account recovery key`,
|
||||
loginMessage: () => t`You are not logged in`,
|
||||
@@ -1441,20 +1421,6 @@ NOTE: Creating a backup with attachments can take a while, and also fail complet
|
||||
],
|
||||
someNotesPublished: () => t`Some notes are published`,
|
||||
unpublishToDelete: () => t`Unpublish notes to delete them`,
|
||||
deleteTags: (count: number) =>
|
||||
plural(count, {
|
||||
one: "Delete tag",
|
||||
other: "Delete # tags"
|
||||
}),
|
||||
deleteTagsConfirm: () => t`Are you sure you want to delete these tags?`,
|
||||
deleteItemConfirmation: (itemType: string) =>
|
||||
t`Are you sure you want to delete this ${strings.dataTypes[
|
||||
itemType as keyof typeof strings.dataTypes
|
||||
]()}?`,
|
||||
deleteItem: (itemType: string) =>
|
||||
t`Delete ${strings.dataTypes[
|
||||
itemType as keyof typeof strings.dataTypes
|
||||
]()}`,
|
||||
filterAttachments: () => t`Filter attachments by filename, type or hash`,
|
||||
oldPassword: () => t`Old password`,
|
||||
newPassword: () => t`New password`,
|
||||
@@ -1485,11 +1451,7 @@ For example:
|
||||
searchANote: () => t`Search a note`,
|
||||
remindeMeOf: () => t`Remind me of...`,
|
||||
addShortNote: () => t`Add a short note`,
|
||||
typeAKeywordToSearchIn: (route: string) =>
|
||||
t`Type a keyword to search in ${strings.routes[
|
||||
route as keyof typeof strings.routes
|
||||
]()}...`,
|
||||
searchingFor: (query: string) => t`Searching for ${query}`,
|
||||
searchingFor: (query: string) => t`Searching for ${query}...`,
|
||||
typeAKeyword: () => t`Type a keyword`,
|
||||
search: () => t`Search`,
|
||||
enterEmailAddress: () => t`Enter email address`,
|
||||
@@ -1511,10 +1473,15 @@ For example:
|
||||
Search: () => t`Search`,
|
||||
Monographs: () => t`Monographs`
|
||||
},
|
||||
searchInRoute: (routeName: string) =>
|
||||
t`Type a keyword to search in ${
|
||||
strings.routes[routeName as keyof typeof strings.routes]?.() || routeName
|
||||
}`,
|
||||
searchInRoute: (
|
||||
routeName: keyof typeof SEARCH_IN_ROUTE_STRINGS | ({} & string)
|
||||
) => {
|
||||
return (
|
||||
SEARCH_IN_ROUTE_STRINGS[
|
||||
routeName as keyof typeof SEARCH_IN_ROUTE_STRINGS
|
||||
]() || t`Search in ${routeName}`
|
||||
);
|
||||
},
|
||||
logoutConfirmation: () =>
|
||||
t`Are you sure you want to logout and clear all data stored on THIS DEVICE?`,
|
||||
backupDataBeforeLogout: () => t`Take a backup before logging out`,
|
||||
@@ -1533,11 +1500,15 @@ For example:
|
||||
t`Please grant notifications permission to add new reminders.`,
|
||||
selectDayError: () => t`Please select the day to repeat the reminder on`,
|
||||
setTitleError: () => t`Please set title of the reminder`,
|
||||
dateError: () => t`Reminder date must be set in future`,
|
||||
dateError: () => t`Reminder time cannot be earlier than the current time.`,
|
||||
failedToDecryptBackup: () => t`Failed to decrypt backup`,
|
||||
backupDirectoryNotSelected: () => t`Backup directory not selected`,
|
||||
legal: () => t`legal`,
|
||||
days: () => t`days`,
|
||||
days: (days: number) =>
|
||||
plural(days, {
|
||||
one: `1 day`,
|
||||
other: `# days`
|
||||
}),
|
||||
daily: () => t`Daily`,
|
||||
weekly: () => t`Weekly`,
|
||||
monthly: () => t`Monthly`,
|
||||
@@ -1568,7 +1539,8 @@ For example:
|
||||
noteLockedBlockLink: () =>
|
||||
t`Linking to a specific block is not available for locked notes.`,
|
||||
dismiss: () => t`Dismiss`,
|
||||
words: () => t`words`,
|
||||
totalWords: (words: number) =>
|
||||
plural(words, { one: "# word", other: "# words" }),
|
||||
addATag: () => t`Add a tag`,
|
||||
startWritingNote: () => t`Start writing your note...`,
|
||||
off: () => t`Off`,
|
||||
@@ -1582,7 +1554,7 @@ For example:
|
||||
restoreFromFiles: () => t`Restore from files`,
|
||||
recentBackups: () => t`RECENT BACKUPS`,
|
||||
restoringBackup: () => t`Restoring backup...`,
|
||||
restoringBackupDesc: () => t`Please wait while we restore your backup`,
|
||||
restoringBackupDesc: () => t`Please wait while we restore your backup...`,
|
||||
decryptingBackup: () => t`Backup is encrypted, decrypting...`,
|
||||
preparingBackupRestore: () => t`Preparing to restore backup file...`,
|
||||
readingBackupFile: () => t`Reading backup file...`,
|
||||
@@ -1608,9 +1580,9 @@ For example:
|
||||
connectedToServer: () => t`Connected to all servers sucessfully.`,
|
||||
allServerUrlsRequired: () => t`All server urls are required.`,
|
||||
serverNotFound: (host: string) => t`Server with host ${host} not found.`,
|
||||
couldNotConnectTo: (server: string) => t`Could not connect to ${server}`,
|
||||
couldNotConnectTo: (server: string) => t`Could not connect to ${server}.`,
|
||||
incorrectServerUrl: (url: string, server: string) =>
|
||||
t`The URL you have given (${url}) does not point to the ${server}`,
|
||||
t`The URL you have given (${url}) does not point to the ${server}.`,
|
||||
serverVersionMismatch: (title: string, url: string) =>
|
||||
t`The ${title} at ${url} is not compatible with this client.`,
|
||||
testConnectionBeforeSave: () =>
|
||||
@@ -1631,9 +1603,9 @@ For example:
|
||||
],
|
||||
restoreThisVersion: () => t`Restore this version`,
|
||||
autoSaveOff: () => t`Auto save: off`,
|
||||
selected: () => t`selected`,
|
||||
selectedWords: (words: number) => plural(words, { other: "# selected" }),
|
||||
dropFilesToAttach: () => t`Drop your files here to attach`,
|
||||
loadingEditor: () => t`Loading editor`,
|
||||
loadingEditor: () => t`Loading editor. Please wait...`,
|
||||
noHeadingsFound: () => t`No headings found`,
|
||||
somethingWentWrong: () => t`Something went wrong`,
|
||||
whatWentWrong: () => t`What went wrong?`,
|
||||
@@ -1671,22 +1643,14 @@ For example:
|
||||
zoomOut: () => t`Zoom out`,
|
||||
zoomIn: () => t`Zoom in`,
|
||||
enterFullScreen: () => t`Enter fullscreen`,
|
||||
syncingYour: (context: string) =>
|
||||
t`Syncing your ${strings.routes[
|
||||
(context.slice(0, 1).toUpperCase() +
|
||||
context.slice(1)) as keyof typeof strings.routes
|
||||
]()}`,
|
||||
syncingYourNotes: () => t`Syncing your notes`,
|
||||
items: () => t`items`,
|
||||
downloadingImages: () => t`Downloading images`,
|
||||
checkingForUpdates: () => t`Checking for updates`,
|
||||
updating: () => t`updating`,
|
||||
restartRequired: () => t`restart required`,
|
||||
available: () => t`available`,
|
||||
itemsRestored: (count: number) =>
|
||||
plural(count, {
|
||||
one: `# item restored`,
|
||||
other: `# items restored`
|
||||
}),
|
||||
updating: (percentage: number) => t`${percentage}% updating...`,
|
||||
updateCompleted: (version: string) =>
|
||||
`v${version} downloaded (click to install)`,
|
||||
updateNewVersionAvailable: (version: string) => t`v${version} available`,
|
||||
unlocking: () => t`Unlocking`,
|
||||
reminderStarts: (date: string, time: string) =>
|
||||
t`The reminder will start on ${date} at ${time}.`,
|
||||
@@ -1801,6 +1765,7 @@ For example:
|
||||
dontShowAgainConfirm: () => t`Don't show again on this device?`,
|
||||
toggleDarkLightMode: () => t`Toggle dark/light mode`,
|
||||
goTo: () => t`Go to`,
|
||||
goToTag: (tag: string) => t`Go to #${tag}`,
|
||||
tagNotFound: () => `Tag not found`,
|
||||
downloadAllAttachments: () => t`Download all attachments`,
|
||||
selectProfilePicture: () => t`Select profile picture`,
|
||||
@@ -1855,7 +1820,8 @@ For example:
|
||||
properties: () => t`Properties`,
|
||||
clickToPreview: () => t`Click to preview`,
|
||||
clearCache: () => t`Clear cache`,
|
||||
clearCacheDesc: () => t`Clear all cached attachments`,
|
||||
clearCacheDesc: (cacheSize: number) =>
|
||||
t`Clear all cached attachments. Current cache size: ${cacheSize}`,
|
||||
clearCacheConfirm: () => t`Clear attachments cache?`,
|
||||
clearCacheConfirmDesc:
|
||||
() => t`Clearing attachments cache will perform the following actions:
|
||||
@@ -1898,94 +1864,22 @@ All attachments will be downloaded & cached again on access.
|
||||
backingUpDataWait: () =>
|
||||
t`We are creating a backup of your data. Please wait...`,
|
||||
resetAccountPassword: () => t`Reset account password`,
|
||||
resettingAccountPassword: () => t`Resetting account password`,
|
||||
resettingAccountPassword: (progress: number) =>
|
||||
t`Resetting account password (${progress})`,
|
||||
resetPasswordWait: () => t`Please wait while we reset your account password.`,
|
||||
recoverySuccess: () => t`Recovery successful!`,
|
||||
recoverySuccessDesc: () => t`Your account has been recovered.`,
|
||||
upgradeNow: () => t`Upgrade now`,
|
||||
backupSavedAt: (path: string) => t`Backup saved at ${path}`,
|
||||
movedToTrash: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `1 ${strings.dataTypes[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} moved to trash`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()} moved to trash`
|
||||
}),
|
||||
irreverisibleAction: () => t`This action is IRREVERSIBLE.`,
|
||||
doAction: (type: string, count: number, action: DoActions) =>
|
||||
plural(count, {
|
||||
one: `${doActions[action]()} ${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()}?`,
|
||||
other: `${doActions[action]()} # ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()}?`
|
||||
}),
|
||||
action: (type: string, count: number, action: Actions) =>
|
||||
plural(count, {
|
||||
one: `${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} ${actions[action]()}`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()} ${actions[action]()}`
|
||||
}),
|
||||
inProgressAction: (type: string, count: number, action: InProgressActions) =>
|
||||
plural(count, {
|
||||
one: `${inProgressActions[action]()} ${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()}`,
|
||||
other: `${inProgressActions[action]()} # ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()}`
|
||||
}),
|
||||
actionError: (type: string, count: number, action: Actions) =>
|
||||
plural(count, {
|
||||
one: `${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} could not be ${actions[action]}`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()} could not be ${actions[action]}`
|
||||
}),
|
||||
deleted: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} deleted`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()} deleted`
|
||||
}),
|
||||
unpinned: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `${strings.dataTypesCamelCase[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()} unpinned`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()} unpinned`
|
||||
}),
|
||||
deleting: () => t`Deleting`,
|
||||
deletingItems: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `Deleting ${strings.dataTypes[
|
||||
type as keyof typeof strings.dataTypes
|
||||
]()}`,
|
||||
other: `Deleting ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()}`
|
||||
}),
|
||||
|
||||
itemsPlural: (type: string, count: number) =>
|
||||
plural(count, {
|
||||
one: `${strings.dataTypes[type as keyof typeof strings.dataTypes]()}`,
|
||||
other: `# ${strings.dataTypesPlural[
|
||||
type as keyof typeof strings.dataTypesPlural
|
||||
]()}`
|
||||
}),
|
||||
doActions,
|
||||
actions,
|
||||
inProgressActions,
|
||||
actionErrors,
|
||||
actionConfirmations,
|
||||
|
||||
deleting: () => t`Deleting`,
|
||||
backupReadyToDownload: () => t`Your backup is ready to download`,
|
||||
vaultUnlocked: () => t`Vault unlocked`,
|
||||
vaultLocked: () => t`Vault locked`,
|
||||
@@ -2008,23 +1902,42 @@ All attachments will be downloaded & cached again on access.
|
||||
},
|
||||
noteDoesNotExist: () => t`Note does not exist`,
|
||||
couldNotConvertNote: (format: string) =>
|
||||
t`Could not convert note to ${format}`,
|
||||
t`Could not convert note to ${format}.`,
|
||||
remindersNotSupported: () =>
|
||||
t`Reminders will not be active on this device as it does not support notifications.`,
|
||||
invalidHexColor: () => t`Please enter a valid hex color (e.g. #ffffff)`,
|
||||
profileUpdated: () => t`Profile updated`,
|
||||
addedToNotebook: (count: number) =>
|
||||
plural(count, {
|
||||
one: `added to 1 notebook`,
|
||||
other: `added to # notebooks`
|
||||
}),
|
||||
removedFromNotebook: (count: number) =>
|
||||
plural(count, {
|
||||
one: `removed from 1 notebook`,
|
||||
other: `removed from # notebooks`
|
||||
}),
|
||||
assignedToNotebookMessage: (
|
||||
notes: number,
|
||||
added: number,
|
||||
removed: number
|
||||
) => {
|
||||
const n = plural(notes, {
|
||||
one: `1 note`,
|
||||
other: `# notes`
|
||||
});
|
||||
if (added === 0 && removed > 0)
|
||||
return t`${n} ${plural(removed, {
|
||||
one: `removed from 1 notebook`,
|
||||
other: `removed from # notebooks`
|
||||
})}.`;
|
||||
else if (added > 0 && removed === 0)
|
||||
return t`${n} ${plural(added, {
|
||||
one: `added to 1 notebook`,
|
||||
other: `added to # notebooks`
|
||||
})}.`;
|
||||
else
|
||||
return t`${n} ${plural(added, {
|
||||
one: `added to 1 notebook`,
|
||||
other: `added to # notebooks`
|
||||
})} and ${plural(removed, {
|
||||
one: `removed from 1 notebook`,
|
||||
other: `removed from # notebooks`
|
||||
})}.`;
|
||||
},
|
||||
noEncryptionKeyFound: () => t`No encryption key found`,
|
||||
couldNotActivateTrial: () => t`Could not activate trial`,
|
||||
couldNotActivateTrial: () =>
|
||||
t`Could not activate trial. Please try again later.`,
|
||||
pleaseTryAgain: () => t`Please try again`,
|
||||
securityKeyRegistered: () => t`Security key successfully registered.`,
|
||||
restartNow: () => t`Restart now`,
|
||||
@@ -2036,13 +1949,13 @@ All attachments will be downloaded & cached again on access.
|
||||
subCanceled: () => t`Your subscription has been canceled.`,
|
||||
refundIssued: () =>
|
||||
t`Your refund has been issued. Please wait 24 hours before reaching out to us in case you do not receive your funds.`,
|
||||
failedToInstallTheme: () => t`Failed to install theme`,
|
||||
failedToInstallTheme: () => t`Failed to install theme.`,
|
||||
fullNameUpdated: () => t`Full name updated`,
|
||||
shortcutRemoved: () => t`Shortcut removed`,
|
||||
recheckFailed: () => t`Rechecking failed`,
|
||||
failedToDelete: () => t`Failed to delete`,
|
||||
failedToRegisterTask: () => t`Failed to register task`,
|
||||
couldNotClearTrash: () => t`Could not clear trash`,
|
||||
couldNotClearTrash: () => t`Could not clear trash.`,
|
||||
automaticBackupsDisabled: () => t`Automatic backups disabled`,
|
||||
automaticBackupsDisabledDesc: () =>
|
||||
t`Please upgrade to Pro to enable automatic backups.`,
|
||||
@@ -2069,7 +1982,7 @@ All attachments will be downloaded & cached again on access.
|
||||
rotateRight: () => t`Rotate right`,
|
||||
website: () => t`Website`,
|
||||
resetSidebar: () => t`Reset sidebar`,
|
||||
removeColor: () => t`Remove color`,
|
||||
removeColor: () => doActions.remove.color(1),
|
||||
favorite: () => t`Favorite`,
|
||||
assignColor: () => t`Assign color`,
|
||||
print: () => t`Print`,
|
||||
@@ -2084,7 +1997,8 @@ All attachments will be downloaded & cached again on access.
|
||||
editingTagDesc: (tag: string) => t`You are editing #${tag}`,
|
||||
applyingChanges: () => t`Applying changes`,
|
||||
thisMayTakeAWhile: () => t`This may take a while`,
|
||||
processing: () => t`Processing`,
|
||||
processing: () => t`Processing...`,
|
||||
processingCollection: (collection: string) => t`Processing ${collection}...`,
|
||||
root: () => t`Root`,
|
||||
credientials: () => t`Credentials`,
|
||||
thankYouForReporting: () => t`Thank you for reporting!`,
|
||||
@@ -2440,7 +2354,6 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
passed: () => t`Passed`,
|
||||
failed: () => t`Failed`,
|
||||
cacheClearedDesc: () => t`All cached attachments have been cleared.`,
|
||||
currentCacheSize: (size: string) => t`Current cache size: ${size}`,
|
||||
restoreBackupConfirm: () => t`Restore backup?`,
|
||||
serversConfigurationDesc: () => t`Configure server URLs for Notesnook`,
|
||||
prioritySupport: () => t`Get Priority support`,
|
||||
@@ -2460,5 +2373,11 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
schoolWork: () => t`School work`,
|
||||
schoolWorkDesc: () => t`Everything related to my school in one place.`,
|
||||
recipes: () => t`Recipes`,
|
||||
recipesDesc: () => t`I love cooking and collecting recipes.`
|
||||
recipesDesc: () => t`I love cooking and collecting recipes.`,
|
||||
error: () => t`Error`,
|
||||
deleteContainingNotes: (count: number) =>
|
||||
plural(count, {
|
||||
one: `Move all notes in this notebook to trash`,
|
||||
other: `Move all notes in these notebooks to trash`
|
||||
})
|
||||
};
|
||||
|
||||
@@ -17,10 +17,14 @@ 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 { resolve } from "path";
|
||||
import path, { resolve } from "path";
|
||||
import { defineConfig } from "vite";
|
||||
import swc from "vite-plugin-swc-transform";
|
||||
import dts from "vite-plugin-dts";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
@@ -32,13 +36,17 @@ export default defineConfig({
|
||||
syntax: "typescript",
|
||||
tsx: true
|
||||
},
|
||||
baseUrl: __dirname,
|
||||
paths: {
|
||||
"$src/*": ["src/*"]
|
||||
},
|
||||
experimental: {
|
||||
plugins: [
|
||||
[
|
||||
"@lingui/swc-plugin",
|
||||
{
|
||||
runtimeModules: {
|
||||
i18n: ["./setup", "i18n"]
|
||||
i18n: ["$src/setup", "i18n"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user