Compare commits

..

1 Commits

Author SHA1 Message Date
Abdullah Atta
6a6032d4f4 web: remove saving note taking too long toast 2026-06-09 13:40:56 +05:00
30 changed files with 461 additions and 721 deletions

View File

@@ -140,7 +140,7 @@ android {
if (project.hasProperty("prBuildNumber")) {
versionCode Integer.parseInt(prBuildNumber())
} else {
versionCode 3107
versionCode 3106
}
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { useThemeColors } from "@notesnook/theme";
import { useRef } from "react";
import { useWindowDimensions, View } from "react-native";
import { View } from "react-native";
import { defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import DatePicker from "react-native-date-picker";
@@ -32,10 +32,7 @@ export default function DatePickerComponent(props: {
onCancel: () => void;
}) {
const { colors, isDark } = useThemeColors();
const dateRef = useRef<Date>(dayjs().add(1, "week").toDate());
const { width } = useWindowDimensions();
const dateRef = useRef<Date>(dayjs().add(1, "day").toDate());
return (
<View
style={{
@@ -49,9 +46,6 @@ export default function DatePickerComponent(props: {
}}
>
<DatePicker
style={{
width: width * 0.8 - DefaultAppStyles.GAP * 2
}}
theme={isDark ? "dark" : "light"}
mode="date"
minimumDate={dayjs().add(1, "day").toDate()}

View File

@@ -51,10 +51,7 @@ import DialogButtons from "../../dialog/dialog-buttons";
import DialogHeader from "../../dialog/dialog-header";
import { Toast } from "../../toast";
import { Button } from "../../ui/button";
import FormInput, {
createFormRef,
validators
} from "../../ui/input/form-input";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
@@ -73,6 +70,8 @@ export const VaultDialog: React.FC = () => {
// UI State
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [wrongPassword, setWrongPassword] = useState(false);
const [passwordsDontMatch, setPasswordsDontMatch] = useState(false);
const [deleteAll, setDeleteAll] = useState(false);
const [biometricUnlock, setBiometricUnlock] = useState(false);
const [isBiometryAvailable, setIsBiometryAvailable] = useState(false);
@@ -101,19 +100,15 @@ export const VaultDialog: React.FC = () => {
| undefined
>(undefined);
// Form ref
const formRef = useRef(
createFormRef({
password: "",
confirmPassword: "",
newPassword: ""
})
);
// Input refs
const passInputRef = useRef<TextInput>(null);
const confirmPassRef = useRef<TextInput>(null);
const newPassInputRef = useRef<TextInput>(null);
const changePassInputRef = useRef<TextInput>(null);
// Password refs
const passwordRef = useRef<string | null>(null);
const confirmPasswordRef = useRef<string | null>(null);
const newPasswordRef = useRef<string | null>(null);
const close = useCallback(() => {
if (loading) {
@@ -128,11 +123,10 @@ export const VaultDialog: React.FC = () => {
Navigation.queueRoutesForUpdate();
// Reset form values and errors
formRef.current.setValue("password", "");
formRef.current.setValue("confirmPassword", "");
formRef.current.setValue("newPassword", "");
formRef.current.clearErrors();
// Reset password refs
passwordRef.current = null;
confirmPasswordRef.current = null;
newPasswordRef.current = null;
// Reset refs
requestTypeRef.current = null;
@@ -150,6 +144,8 @@ export const VaultDialog: React.FC = () => {
// Reset UI state
setVisible(false);
setLoading(false);
setWrongPassword(false);
setPasswordsDontMatch(false);
setDeleteAll(false);
setBiometricUnlock(false);
setIsBiometryAvailable(false);
@@ -159,10 +155,9 @@ export const VaultDialog: React.FC = () => {
const deleteVault = useCallback(async () => {
setLoading(true);
try {
const { password } = formRef.current.getValues();
let verified = true;
if (await db.user.getUser()) {
verified = await db.user.verifyPassword(password);
verified = await db.user.verifyPassword(passwordRef.current || "");
}
if (verified) {
let noteIds: string[] = [];
@@ -180,7 +175,6 @@ export const VaultDialog: React.FC = () => {
noteIds = relations.map((item) => item.toId);
}
await db.vault.delete(deleteAll);
await BiometricService.resetCredentials();
if (deleteAll) {
noteIds.forEach((id) => {
@@ -201,7 +195,11 @@ export const VaultDialog: React.FC = () => {
}, 100);
} else {
setLoading(false);
formRef.current.setError("password", strings.passwordIncorrect());
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
}
} catch (e) {
console.error(e);
@@ -211,12 +209,11 @@ export const VaultDialog: React.FC = () => {
const clearVault = useCallback(async () => {
setLoading(true);
try {
const { password } = formRef.current.getValues();
const vault = await db.vaults.default();
const relations = await db.relations.from(vault!, "note").get();
const noteIds = relations.map((item) => item.toId);
await db.vault.clear(password);
await db.vault.clear(passwordRef.current || "");
noteIds.forEach((id) => {
eSendEvent(
@@ -236,7 +233,11 @@ export const VaultDialog: React.FC = () => {
type: "success"
});
} catch (e) {
formRef.current.setError("password", strings.passwordIncorrect());
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
}
setLoading(false);
}, [close]);
@@ -256,7 +257,12 @@ export const VaultDialog: React.FC = () => {
});
close();
} catch (e) {
formRef.current.setError("password", strings.passwordIncorrect());
close();
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
setLoading(false);
}
},
@@ -264,14 +270,24 @@ export const VaultDialog: React.FC = () => {
);
const takeErrorAction = useCallback(() => {
formRef.current.setError("password", strings.passwordIncorrect());
setWrongPassword(true);
setVisible(true);
setTimeout(() => {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
}, 500);
}, []);
const lockNote = useCallback(async () => {
const { password } = formRef.current.getValues();
if (!password || password.trim() === "") {
formRef.current.setError("password", strings.passwordIncorrect());
if (!passwordRef.current || passwordRef.current.trim() === "") {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
return;
} else {
await db.vault.add(noteRef.current!.id);
@@ -289,9 +305,8 @@ export const VaultDialog: React.FC = () => {
}, [close]);
const permanantUnlock = useCallback(() => {
const { password } = formRef.current.getValues();
db.vault
.remove(noteRef.current!.id, password)
.remove(noteRef.current!.id, passwordRef.current || "")
.then(async () => {
ToastManager.show({
heading: strings.noteUnlocked(),
@@ -300,7 +315,7 @@ export const VaultDialog: React.FC = () => {
});
eSendEvent(eUpdateNoteInEditor, noteRef.current, true);
if (biometricUnlock && !isBiometryEnrolled) {
await enrollFingerprint(password);
await enrollFingerprint(passwordRef.current || "");
}
close();
})
@@ -360,9 +375,8 @@ export const VaultDialog: React.FC = () => {
);
const deleteNote = useCallback(async () => {
const { password } = formRef.current.getValues();
try {
await db.vault.remove(noteRef.current!.id, password);
await db.vault.remove(noteRef.current!.id, passwordRef.current || "");
await deleteItems("note", [noteRef.current!.id]);
close();
} catch (e) {
@@ -371,14 +385,16 @@ export const VaultDialog: React.FC = () => {
}, [close, takeErrorAction]);
const openNote = useCallback(async () => {
const { password } = formRef.current.getValues();
try {
if (!password) throw new Error("Invalid password");
if (!passwordRef.current) throw new Error("Invalid password");
const note = await db.vault.open(noteRef.current!.id, password);
const note = await db.vault.open(
noteRef.current!.id,
passwordRef.current
);
if (!note) throw new Error("Failed to unlock note.");
if (biometricUnlock && !isBiometryEnrolled) {
await enrollFingerprint(password);
await enrollFingerprint(passwordRef.current || "");
}
const requestType = requestTypeRef.current;
@@ -395,6 +411,7 @@ export const VaultDialog: React.FC = () => {
requestType === VaultRequestType.CustomAction &&
onUnlockRef.current
) {
const password = passwordRef.current;
const unlock = onUnlockRef.current;
close();
await sleep(500);
@@ -416,9 +433,12 @@ export const VaultDialog: React.FC = () => {
]);
const unlockNote = useCallback(async () => {
const { password } = formRef.current.getValues();
if (!password || password.trim() === "") {
formRef.current.setError("password", strings.passwordIncorrect());
if (!passwordRef.current || passwordRef.current.trim() === "") {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
return;
}
if (requestTypeRef.current === VaultRequestType.PermanentUnlock) {
@@ -429,11 +449,10 @@ export const VaultDialog: React.FC = () => {
}, [permanantUnlock, openNote]);
const createVault = useCallback(async () => {
const { password } = formRef.current.getValues();
await db.vault.create(password);
await db.vault.create(passwordRef.current || "");
if (biometricUnlock) {
await enrollFingerprint(password);
await enrollFingerprint(passwordRef.current || "");
}
if (noteRef.current?.id) {
await db.vault.add(noteRef.current.id);
@@ -485,23 +504,36 @@ export const VaultDialog: React.FC = () => {
if (loading) return;
if (!formRef.current.validate()) return;
const { password, newPassword } = formRef.current.getValues();
if (!passwordRef.current) {
ToastManager.show({
heading: strings.passwordNotEntered(),
type: "error",
context: "local"
});
return;
}
if (requestType === VaultRequestType.CreateVault) {
if (passwordRef.current !== confirmPasswordRef.current) {
ToastManager.show({
heading: strings.passwordNotMatched(),
type: "error",
context: "local"
});
setPasswordsDontMatch(true);
return;
}
createVault();
} else if (requestType === VaultRequestType.ChangePassword) {
setLoading(true);
db.vault
.changePassword(password, newPassword)
.then(async () => {
.changePassword(passwordRef.current, newPasswordRef.current || "")
.then(() => {
setLoading(false);
if (biometricUnlock) {
enrollFingerprint(newPassword);
} else {
await BiometricService.resetCredentials();
enrollFingerprint(newPasswordRef.current || "");
}
ToastManager.show({
heading: strings.passwordUpdated(),
@@ -513,23 +545,37 @@ export const VaultDialog: React.FC = () => {
.catch((e) => {
setLoading(false);
if (e.message === VAULT_ERRORS.wrongPassword) {
formRef.current.setError("password", strings.passwordIncorrect());
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
} else {
console.error(e);
ToastManager.error(e);
}
});
} else if (requestType === VaultRequestType.LockNote) {
if (!passwordRef.current || passwordRef.current.trim() === "") {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
setWrongPassword(true);
return;
}
db.vault
.unlock(password)
.unlock(passwordRef.current)
.then(async (unlocked) => {
if (unlocked) {
setWrongPassword(false);
await lockNote();
} else {
formRef.current.setError("password", strings.passwordIncorrect());
takeErrorAction();
}
})
.catch((e) => {
formRef.current.setError("password", strings.passwordIncorrect());
takeErrorAction();
});
} else if (
requestType === VaultRequestType.UnlockNote ||
@@ -540,13 +586,22 @@ export const VaultDialog: React.FC = () => {
requestType === VaultRequestType.DeleteNote ||
requestType === VaultRequestType.CustomAction
) {
if (!passwordRef.current || passwordRef.current.trim() === "") {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
context: "local"
});
setWrongPassword(true);
return;
}
if (noteLockedRef.current) {
await unlockNote();
} else {
console.log("Error: Note should be locked for this operation");
}
} else if (requestType === VaultRequestType.EnableFingerprint) {
enrollFingerprint(password);
enrollFingerprint(passwordRef.current);
} else if (requestType === VaultRequestType.ClearVault) {
await clearVault();
} else if (requestType === VaultRequestType.DeleteVault) {
@@ -561,6 +616,7 @@ export const VaultDialog: React.FC = () => {
enrollFingerprint,
unlockNote,
lockNote,
takeErrorAction,
clearVault,
deleteVault
]);
@@ -576,7 +632,7 @@ export const VaultDialog: React.FC = () => {
if (!credentials) throw new Error("Failed to get user credentials");
if (credentials?.password) {
formRef.current.setValue("password", credentials.password);
passwordRef.current = credentials.password;
onPress();
} else {
eSendEvent(eCloseActionSheet);
@@ -623,6 +679,8 @@ export const VaultDialog: React.FC = () => {
setIsBiometryAvailable(available);
setIsBiometryEnrolled(fingerprint);
setBiometricUnlock(fingerprint);
setWrongPassword(false);
setPasswordsDontMatch(false);
setDeleteAll(false);
setLoading(false);
@@ -714,14 +772,14 @@ export const VaultDialog: React.FC = () => {
isCustomAction) &&
!isRevokeFingerprint ? (
<>
<FormInput
name="password"
formRef={formRef}
<Input
fwdRef={passInputRef}
editable={!loading}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwd}
autoComplete="password"
onChangeText={(value) => {
passwordRef.current = value;
}}
marginBottom={
!biometricUnlock ||
!isBiometryEnrolled ||
@@ -731,13 +789,14 @@ export const VaultDialog: React.FC = () => {
? 0
: 10
}
onSubmitEditing={() => {
onSubmit={() => {
if (isChangePassword) {
newPassInputRef.current?.focus();
confirmPassRef.current?.focus();
} else {
onPress();
}
}}
autoComplete="password"
returnKeyLabel={
isChangePassword ? strings.next() : titleRef.current
}
@@ -748,7 +807,6 @@ export const VaultDialog: React.FC = () => {
? strings.currentPassword()
: strings.password()
}
validators={[validators.required(strings.passwordRequired())]}
/>
{!biometricUnlock ||
@@ -791,67 +849,70 @@ export const VaultDialog: React.FC = () => {
{isChangePassword ? (
<>
<Seperator half />
<FormInput
name="newPassword"
formRef={formRef}
fwdRef={newPassInputRef}
<Input
fwdRef={confirmPassRef}
editable={!loading}
testID={notesnook.ids.dialogs.vault.changePwd}
autoCapitalize="none"
onChangeText={(value) => {
newPasswordRef.current = value;
}}
autoComplete="password"
onSubmitEditing={() => {
onSubmit={() => {
onPress();
}}
returnKeyLabel="Change"
returnKeyType="done"
secureTextEntry
placeholder={strings.newPassword()}
validators={[validators.required(strings.passwordRequired())]}
/>
</>
) : null}
{isCreateVault ? (
<View>
<FormInput
name="password"
formRef={formRef}
<Input
fwdRef={passInputRef}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwd}
onChangeText={(value) => {
passwordRef.current = value;
}}
autoComplete="password"
returnKeyLabel={strings.next()}
returnKeyType="next"
secureTextEntry
onSubmitEditing={() => {
onSubmit={() => {
confirmPassRef.current?.focus();
}}
placeholder={strings.password()}
validators={[validators.required(strings.passwordRequired())]}
/>
<FormInput
name="confirmPassword"
formRef={formRef}
<Input
fwdRef={confirmPassRef}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwdAlt}
secureTextEntry
validationType="confirmPassword"
customValidator={() => passwordRef.current || ""}
errorMessage="Passwords do not match."
onErrorCheck={() => null}
marginBottom={0}
autoComplete="password"
returnKeyLabel="Create"
returnKeyType="done"
marginBottom={0}
onSubmitEditing={() => {
onChangeText={(value) => {
confirmPasswordRef.current = value;
if (value !== passwordRef.current) {
setPasswordsDontMatch(true);
} else {
setPasswordsDontMatch(false);
}
}}
onSubmit={() => {
onPress();
}}
placeholder={strings.confirmPassword()}
validators={[
validators.required(strings.confirmPasswordRequired()),
validators.matchField(
"password",
strings.passwordNotMatched()
)
]}
/>
</View>
) : null}

View File

@@ -55,7 +55,6 @@ import { TimeSince } from "../../ui/time-since";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import dayjs from "dayjs";
import { ExpiryDate } from "../../ui/expiry-date";
type NoteItemProps = {
item: Note | BaseTrashItem<Note>;
@@ -267,21 +266,6 @@ const NoteItem = ({
/>
) : null}
{item.expiryDate?.value ? (
<ExpiryDate
note={item as Note}
color={color?.colorCode}
textStyle={{ fontSize: AppFontSize.xxs }}
short
iconSize={AppFontSize.xxs}
style={{
justifyContent: "flex-start",
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2,
alignSelf: "flex-start"
}}
/>
) : null}
{notebooks?.items
?.filter(
(item) =>

View File

@@ -110,7 +110,6 @@ export const DateMeta = ({ item }: { item: Item }) => {
}}
maximumDate={new Date((item as Note).dateEdited)}
isDarkModeEnabled={isDark}
themeVariant={isDark ? "dark" : "light"}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={new Date(dateCreated)}
/>

View File

@@ -54,7 +54,6 @@ import FormInput, {
createFormRef,
validators
} from "../../ui/input/form-input";
import { useAppState } from "../../../../app/hooks/use-app-state";
async function fetchMonographData(noteId: string) {
const monographId = db.monographs.monograph(noteId);
@@ -88,25 +87,14 @@ const PublishNoteSheet = ({
const monograph = monographData.result?.monograph;
const publishUrl = monograph && `${hosts.MONOGRAPH_HOST}/${monograph?.id}`;
const isPublished = db.monographs.monograph(note?.id);
const appState = useAppState();
const previousAppState = useRef(appState);
const formRef = useRef(
createFormRef({
title: note.title || "",
title: monograph?.title || note.title || "",
password: ""
})
);
useEffect(() => {
if (!monographData.result) return;
const title = monograph?.title || note.title || "";
formRef.current.setValue("title", title);
setTimeout(() => {
titleInput.current?.setNativeProps({ text: title });
}, 50);
}, [monographData.result, monograph]);
useEffect(() => {
(async () => {
if (monograph) {
@@ -180,35 +168,6 @@ const PublishNoteSheet = ({
setPublishLoading(false);
};
const lastAnalyticsResult = useRef<Awaited<
ReturnType<typeof db.monographs.analytics>
> | null>(null);
const analytics = useAsync(async () => {
if (!isFeatureAvailable?.isAllowed || !monograph?.id || selfDestruct)
return null;
const result = await db.monographs.analytics(monograph.id);
if (result) lastAnalyticsResult.current = result;
return result;
}, [monograph?.id, isFeatureAvailable?.isAllowed, selfDestruct]);
const analyticsData = analytics.result ?? lastAnalyticsResult.current;
useEffect(() => {
const prevState = previousAppState.current;
previousAppState.current = appState;
if (
appState === "active" &&
prevState !== "active" &&
isFeatureAvailable?.isAllowed &&
monograph?.id &&
!selfDestruct
) {
analytics.execute();
}
}, [appState, monograph?.id, selfDestruct, isFeatureAvailable?.isAllowed]);
return (
<View
style={{
@@ -418,10 +377,7 @@ const PublishNoteSheet = ({
</View>
</TouchableOpacity>
{isFeatureAvailable?.isAllowed &&
!selfDestruct &&
analyticsData &&
analyticsData?.totalViews > 0 ? (
{isFeatureAvailable?.isAllowed ? (
<View
style={{
flexDirection: "row",
@@ -445,10 +401,6 @@ const PublishNoteSheet = ({
}}
>
<Paragraph size={AppFontSize.sm}>{strings.views()}</Paragraph>
<Paragraph size={AppFontSize.sm}>
{analyticsData?.totalViews}
</Paragraph>
</View>
</View>
</View>

View File

@@ -1,71 +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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Note } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { TextStyle, ViewStyle } from "react-native";
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { Button, ButtonProps } from "../button";
import dayjs from "dayjs";
export const ExpiryDate = ({
note,
color,
style,
textStyle,
iconSize,
short,
...props
}: {
note: Note;
color?: string;
style?: ViewStyle;
textStyle?: TextStyle;
iconSize?: number;
short?: boolean;
} & ButtonProps) => {
const { colors } = useThemeColors();
const expiryValue = note?.expiryDate?.value;
if (!expiryValue) return null;
const formattedDate = dayjs(expiryValue).format("DD MMM YYYY");
return (
<Button
title={formattedDate}
icon="bomb"
fontSize={textStyle?.fontSize || AppFontSize.xs}
iconSize={iconSize || AppFontSize.sm}
type="secondary"
textStyle={{
marginRight: 0,
...textStyle
}}
style={{
height: "auto",
borderRadius: defaultBorderRadius,
borderColor: colors.primary.border,
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
...(style as ViewStyle)
}}
{...props}
/>
);
};

View File

@@ -69,11 +69,7 @@ import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { useTagStore } from "../stores/use-tag-store";
import { useUserStore } from "../stores/use-user-store";
import {
eCloseSheet,
eMenuItemUpdate,
eUpdateNoteInEditor
} from "../utils/events";
import { eCloseSheet, eUpdateNoteInEditor } from "../utils/events";
import { deleteItems } from "../utils/functions";
import { convertNoteToText } from "../utils/note-to-text";
import { NotesnookModule } from "../utils/notesnook-module";
@@ -1184,22 +1180,12 @@ export const useActions = ({
},
{
id: "expiry-date",
title: item.expiryDate?.value
? strings.unsetExpiry()
: strings.setExpiry(),
icon: item.expiryDate?.value ? "bomb-off" : "bomb",
title: item.expiryDate ? strings.unsetExpiry() : strings.setExpiry(),
icon: item.expiryDate ? "bomb-off" : "bomb",
locked: !features?.expiringNotes?.isAllowed,
onPress: async () => {
if (item.expiryDate?.value) {
if (item.expiryDate) {
await db.notes.setExpiryDate(null, item.id);
Navigation.queueRoutesForUpdate();
eSendEvent(eMenuItemUpdate);
ToastManager.show({
message: strings.expiryDateRemoved(),
type: "success",
context: "local"
});
setItem((await db.notes.note(item.id)) as Item);
} else {
if (features && !features?.expiringNotes.isAllowed) {
@@ -1223,14 +1209,6 @@ export const useActions = ({
onConfirm={async (date) => {
close?.();
await db.notes.setExpiryDate(date.getTime(), item.id);
Navigation.queueRoutesForUpdate();
eSendEvent(eMenuItemUpdate);
ToastManager.show({
message: strings.expiryDateSet(),
type: "success",
context: "local"
});
setItem((await db.notes.note(item.id)) as Item);
}}
/>

View File

@@ -64,7 +64,6 @@ import { strings } from "@notesnook/intl";
import { i18n } from "@lingui/core";
import { useVaultStatus } from "../../hooks/use-vault-status";
import { useSettingStore } from "../../stores/use-setting-store";
import { NativeEvents } from "@notesnook/editor-mobile/src/utils/native-events";
const style: ViewStyle = {
height: "100%",
@@ -269,22 +268,17 @@ const useLockedNoteHandler = () => {
const onSubmit = async ({
password,
biometrics: enrollBiometrics,
resolverId
biometrics: enrollBiometrics
}: {
password: string;
biometrics?: boolean;
resolverId?: string;
}) => {
if (!tabRef.current?.session?.noteId || !tabRef.current) return;
if (!password || password.trim().length === 0) {
if (resolverId) {
editorController.current?.postMessage(NativeEvents.resolve, {
resolverId,
data: { success: false, error: strings.passwordNotEntered() }
});
}
ToastManager.show({
heading: strings.passwordNotEntered(),
type: "error"
});
return;
}
@@ -293,7 +287,6 @@ const useLockedNoteHandler = () => {
tabRef.current?.session?.noteId,
password
);
if (enrollBiometrics && note) {
try {
const unlocked = await db.vault.unlock(password);
@@ -305,6 +298,7 @@ const useLockedNoteHandler = () => {
type: "success",
context: "global"
});
const biometry = await BiometricService.isBiometryAvailable();
const fingerprint = await BiometricService.hasInternetCredentials();
useTabStore.setState({
@@ -312,24 +306,22 @@ const useLockedNoteHandler = () => {
biometryEnrolled: !!fingerprint
});
syncTabs();
} catch (e) {}
} catch (e) {
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error"
});
}
}
if (resolverId) {
editorController.current?.postMessage(NativeEvents.resolve, {
resolverId,
data: { success: true }
});
}
eSendEvent(eOnLoadNote, { item: note, refresh: true });
eSendEvent(eOnLoadNote, {
item: note,
refresh: true
});
} catch (e) {
if (resolverId) {
editorController.current?.postMessage(NativeEvents.resolve, {
resolverId,
data: { success: false, error: strings.passwordIncorrect() }
});
}
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error"
});
}
};

View File

@@ -721,10 +721,7 @@ export const useEditorEvents = (
}
case EditorEvents.unlock: {
eSendEvent(eUnlockWithPassword, {
...editorMessage.value,
resolverId: editorMessage.resolverId
});
eSendEvent(eUnlockWithPassword, editorMessage.value);
break;
}

View File

@@ -1718,7 +1718,7 @@ export const settingsGroups: SettingSection[] = [
console.error(e);
}
},
description: Platform.OS === "ios" ? "3.3.27" : getVersion()
description: Platform.OS === "ios" ? "3.3.26" : getVersion()
}
]
}

View File

@@ -86,13 +86,11 @@ function ThemeSelector() {
const { colors } = useThemeColors();
const themeColors = colors;
const [searchQuery, setSearchQuery] = useState<string>();
const [colorScheme, setColorScheme] = useState<"all" | "dark" | "light">(
"all"
);
const [colorScheme, setColorScheme] = useState<string>();
const filters = [];
if (searchQuery) filters.push({ type: "term" as const, value: searchQuery });
if (colorScheme !== "all")
if (colorScheme)
filters.push({ type: "colorScheme" as const, value: colorScheme });
const themes = trpc.themes.useInfiniteQuery(
@@ -107,14 +105,6 @@ function ThemeSelector() {
}
);
if (themes?.isError) {
DatabaseLogger.error(
new Error(themes.error.message),
"themes loading error",
themes.error?.data || undefined
);
}
const select = (item: Partial<ThemeMetadata>, fromFile?: boolean) => {
presentSheet({
context: "theme-details",
@@ -124,209 +114,204 @@ function ThemeSelector() {
});
};
const renderItem = React.useCallback(
({ item, index }: { item: ThemeMetadata; index: number }) => {
const colors =
item.previewColors ||
getPreviewColors(item as unknown as ThemeDefinition);
const renderItem = ({
item,
index
}: {
item: ThemeMetadata;
index: number;
}) => {
const colors =
item.previewColors ||
getPreviewColors(item as unknown as ThemeDefinition);
return (
<>
<TouchableOpacity
activeOpacity={0.9}
return (
<>
<TouchableOpacity
activeOpacity={0.9}
style={{
borderRadius: 10,
padding: DefaultAppStyles.GAP_SMALL,
marginBottom: DefaultAppStyles.GAP_VERTICAL,
flexShrink: 1,
marginHorizontal: 10
}}
onPress={() => select(item)}
>
<View
style={{
backgroundColor: colors?.background,
height: 200,
width: "100%",
borderRadius: 10,
padding: DefaultAppStyles.GAP_SMALL,
marginBottom: DefaultAppStyles.GAP_VERTICAL,
flexShrink: 1,
marginHorizontal: 10
overflow: "hidden",
flexDirection: "row",
justifyContent: "space-between",
...getElevationStyle(3)
}}
onPress={() => select(item)}
>
<View
style={{
backgroundColor: colors?.background,
height: 200,
width: "100%",
borderRadius: 10,
marginBottom: DefaultAppStyles.GAP_VERTICAL,
overflow: "hidden",
flexDirection: "row",
justifyContent: "space-between",
...getElevationStyle(3)
height: "100%",
width: "49.5%",
backgroundColor: colors.navigationMenu.background,
padding: DefaultAppStyles.GAP_SMALL,
paddingVertical: 3,
borderRadius: defaultBorderRadius
}}
>
{MenuItemsList.map((item, index) => (
<View
key={item.id}
style={{
height: 12,
width: "100%",
backgroundColor:
index === 0
? colors.navigationMenu.accent + 40
: colors.navigationMenu.background,
borderRadius: 2,
paddingHorizontal: 3,
flexDirection: "row",
alignItems: "center",
marginBottom: 4
}}
>
<Icon
size={8}
name={item.icon}
color={
index === 0
? colors.navigationMenu.accent
: colors.navigationMenu.icon
}
/>
<View
style={{
height: 3,
width: "40%",
backgroundColor:
index === 0
? colors.navigationMenu.accent
: colors.paragraph,
borderRadius: 2,
marginLeft: 3
}}
></View>
</View>
))}
</View>
<View
style={{
height: "100%",
width: "49.5%",
backgroundColor: colors.list.background,
borderRadius: defaultBorderRadius,
paddingHorizontal: 2,
paddingRight: 6
}}
>
<View
style={{
height: "100%",
width: "49.5%",
backgroundColor: colors.navigationMenu.background,
padding: DefaultAppStyles.GAP_SMALL,
paddingVertical: 3,
borderRadius: defaultBorderRadius
}}
>
{MenuItemsList.map((item, index) => (
<View
key={item.id}
style={{
height: 12,
width: "100%",
backgroundColor:
index === 0
? colors.navigationMenu.accent + 40
: colors.navigationMenu.background,
borderRadius: 2,
paddingHorizontal: 3,
flexDirection: "row",
alignItems: "center",
marginBottom: 4
}}
>
<Icon
size={8}
name={item.icon}
color={
index === 0
? colors.navigationMenu.accent
: colors.navigationMenu.icon
}
/>
<View
style={{
height: 3,
width: "40%",
backgroundColor:
index === 0
? colors.navigationMenu.accent
: colors.paragraph,
borderRadius: 2,
marginLeft: 3
}}
></View>
</View>
))}
</View>
<View
style={{
height: "100%",
width: "49.5%",
backgroundColor: colors.list.background,
borderRadius: defaultBorderRadius,
paddingHorizontal: 2,
paddingRight: 6
height: 12,
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginTop: 3
}}
>
<View
style={{
height: 12,
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginTop: 3
alignItems: "center"
}}
>
<View
<Icon size={8} color={colors.list.heading} name="menu" />
<Heading
style={{
flexDirection: "row",
alignItems: "center"
marginLeft: 3
}}
color={colors.list.heading}
size={7}
>
<Icon size={8} color={colors.list.heading} name="menu" />
<Heading
style={{
marginLeft: 3
}}
color={colors.list.heading}
size={7}
>
{strings.dataTypesPluralCamelCase.note()}
</Heading>
</View>
<Icon name="magnify" color={colors.list.heading} size={7} />
{strings.dataTypesPluralCamelCase.note()}
</Heading>
</View>
</View>
<View
style={{
width: "100%",
alignItems: "flex-end",
justifyContent: "flex-end",
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL,
position: "absolute",
bottom: 6,
right: 6,
flexDirection: "row",
gap: 10
}}
>
{darkTheme.id === item.id || lightTheme.id === item.id ? (
<IconButton
name="check"
type="plain"
style={{
borderRadius: 100,
paddingHorizontal: 6,
alignSelf: "flex-end",
width: 25,
height: 25
}}
color={colors.accent}
size={16}
/>
) : null}
<Button
title={
item.colorScheme === "dark"
? strings.dark()
: strings.light()
}
type="secondaryAccented"
height={25}
buttonType={{
color: item.colorScheme === "dark" ? "black" : "#f0f0f060",
text: colors.accent
}}
style={{
borderRadius: 100,
paddingHorizontal: DefaultAppStyles.GAP,
alignSelf: "flex-end",
borderColor:
item.colorScheme === "dark"
? getColorLinearShade("#000000", 0.1, true)
: getColorLinearShade("#f0f0f0", 0.1, true)
}}
fontSize={AppFontSize.xxs}
/>
<Icon name="magnify" color={colors.list.heading} size={7} />
</View>
</View>
<Heading size={AppFontSize.sm} color={themeColors.primary.heading}>
{item.name}
</Heading>
<Paragraph
size={AppFontSize.xs}
color={themeColors.secondary?.paragraph}
<View
style={{
width: "100%",
alignItems: "flex-end",
justifyContent: "flex-end",
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL,
position: "absolute",
bottom: 6,
right: 6,
flexDirection: "row",
gap: 10
}}
>
{strings.by()} {item.authors?.[0].name}
</Paragraph>
</TouchableOpacity>
</>
);
},
[
darkTheme.id,
lightTheme.id,
themeColors.primary.heading,
themeColors.secondary?.paragraph
]
);
{darkTheme.id === item.id || lightTheme.id === item.id ? (
<IconButton
name="check"
type="plain"
style={{
borderRadius: 100,
paddingHorizontal: 6,
alignSelf: "flex-end",
width: 25,
height: 25
}}
color={colors.accent}
size={16}
/>
) : null}
<Button
title={
item.colorScheme === "dark" ? strings.dark() : strings.light()
}
type="secondaryAccented"
height={25}
buttonType={{
color: item.colorScheme === "dark" ? "black" : "#f0f0f060",
text: colors.accent
}}
style={{
borderRadius: 100,
paddingHorizontal: DefaultAppStyles.GAP,
alignSelf: "flex-end",
borderColor:
item.colorScheme === "dark"
? getColorLinearShade("#000000", 0.1, true)
: getColorLinearShade("#f0f0f0", 0.1, true)
}}
fontSize={AppFontSize.xxs}
/>
</View>
</View>
<Heading size={AppFontSize.sm} color={themeColors.primary.heading}>
{item.name}
</Heading>
<Paragraph
size={AppFontSize.xs}
color={themeColors.secondary?.paragraph}
>
{strings.by()} {item.authors?.[0].name}
</Paragraph>
</TouchableOpacity>
</>
);
};
let resetTimer: NodeJS.Timeout;
const onSearch = (text: string) => {
clearTimeout(resetTimer as NodeJS.Timeout);
@@ -346,14 +331,11 @@ function ThemeSelector() {
.flat()
.filter((theme) =>
searchQuery && searchQuery !== ""
? colorScheme === "all" || colorScheme === theme.colorScheme
: darkTheme.id !== theme.id &&
lightTheme.id !== theme.id &&
(colorScheme === "all" || colorScheme === theme.colorScheme)
? true
: darkTheme.id !== theme.id && lightTheme.id !== theme.id
) || []
);
}
return (
<>
<SheetProvider context="theme-details" />
@@ -391,12 +373,12 @@ function ThemeSelector() {
paddingHorizontal: DefaultAppStyles.GAP_SMALL
}}
type={
colorScheme === "all" || !colorScheme ? "accent" : "secondary"
colorScheme === "" || !colorScheme ? "accent" : "secondary"
}
title={strings.all()}
fontSize={AppFontSize.xs}
onPress={() => {
setColorScheme("all");
setColorScheme("");
}}
/>
<Button
@@ -519,21 +501,15 @@ function ThemeSelector() {
<LegendList
numColumns={2}
data={
themes.isLoading || themes.isError
data={[
...(colorScheme === "dark" || (searchQuery && searchQuery !== "")
? []
: [
...(colorScheme === "dark" ||
(searchQuery && searchQuery !== "")
? []
: [lightTheme as unknown as ThemeMetadata]),
...(colorScheme === "light" ||
(searchQuery && searchQuery !== "")
? []
: [darkTheme as unknown as ThemeMetadata]),
...getThemes()
]
}
: [lightTheme as unknown as ThemeMetadata]),
...(colorScheme === "light" || (searchQuery && searchQuery !== "")
? []
: [darkTheme as unknown as ThemeMetadata]),
...getThemes()
]}
ListEmptyComponent={
<View
style={{
@@ -555,23 +531,20 @@ function ThemeSelector() {
</View>
}
ListFooterComponent={
<View
style={{
height: 100,
width: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
{themes.isError ? (
themes.isError ? (
<View
style={{
height: 100,
width: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
<Paragraph color={colors.error.paragraph}>
{strings.errorLoadingThemes()}. {themes.error.message}.
</Paragraph>
) : (themes.isLoading || themes.isFetching) &&
getThemes().length ? (
<ActivityIndicator color={colors.primary.accent} />
) : null}
</View>
</View>
) : null
}
estimatedItemSize={200}
renderItem={renderItem}

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2186
IOS_MARKETING_VERSION = 3.4.2
IOS_CURRENT_PROJECT_VERSION = 2185
IOS_MARKETING_VERSION = 3.4.1
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2186
IOS_MARKETING_VERSION = 3.4.2
IOS_CURRENT_PROJECT_VERSION = 2185
IOS_MARKETING_VERSION = 3.4.1
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Staging iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2186
IOS_MARKETING_VERSION = 3.4.2
IOS_CURRENT_PROJECT_VERSION = 2185
IOS_MARKETING_VERSION = 3.4.1
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.3.27",
"version": "3.3.25",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.3.27",
"version": "3.3.25",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.3.27",
"version": "3.3.25",
"private": true,
"license": "GPL-3.0-or-later",
"scripts": {

View File

@@ -25,8 +25,7 @@ import {
FeatureId,
FeatureResult,
isFeatureAvailable,
sanitizeFilename,
formatBytes
sanitizeFilename
} from "@notesnook/common";
import { useStore as useUserStore } from "../stores/user-store";
import { useStore as useAppStore } from "../stores/app-store";
@@ -40,7 +39,6 @@ import { readFile, showFilePicker } from "../utils/file-picker";
import { logger } from "../utils/logger";
import { PATHS } from "@notesnook/desktop";
import { TaskManager } from "./task-manager";
import { AppEventManager, AppEvents } from "./app-events";
import { EVENTS } from "@notesnook/core";
import { createWritableStream } from "./desktop-bridge";
import { FeatureDialog, FeatureKeys } from "../dialogs/feature-dialog";
@@ -144,77 +142,46 @@ export async function createBackup(
action: async (report) => {
const { createZipStream } = await import("../utils/streams/zip-stream");
const writeStream = await createWritableStream(filePath);
let currentAttachmentIndex = 0;
const totalAttachments =
mode === "full" ? await db.attachments.all.count() : 0;
const updateAttachmentProgressEvent = AppEventManager.subscribe(
AppEvents.UPDATE_ATTACHMENT_PROGRESS,
(ev: any) => {
if (ev.type === "download" || ev.type === "encrypt") {
const percent = Math.round((ev.loaded / ev.total) * 100);
report({
text: background
? `Creating backup (${ev.hash} ${percent}%)`
: `${
ev.type === "download"
? strings.downloading()
: strings.encrypting()
} attachment ${ev.hash} | ${percent}% ${`(${formatBytes(
ev.loaded
)} / ${formatBytes(ev.total)})`}`,
total: totalAttachments,
current: currentAttachmentIndex + 1
});
}
}
);
try {
await new ReadableStream<ZipFile>({
start() {},
async pull(controller) {
for await (const output of db.backup!.export({
type: "web",
encrypt: encryptedBackups,
mode
})) {
if (output.type === "file") {
const file = output;
report({
text: background
? `Creating backup (${file.path})`
: `Saving file ${file.path}`
});
controller.enqueue({
path: file.path,
data: encoder.encode(file.data)
});
} else if (output.type === "attachment") {
currentAttachmentIndex = output.current;
report({
text: background
? `Creating backup (${output.hash})`
: `Saving attachment ${output.hash}`,
total: output.total,
current: output.current
});
const handle = await streamablefs.readFile(output.hash);
if (!handle) continue;
controller.enqueue({
path: output.path,
data: handle.readable
});
}
await new ReadableStream<ZipFile>({
start() {},
async pull(controller) {
for await (const output of db.backup!.export({
type: "web",
encrypt: encryptedBackups,
mode
})) {
if (output.type === "file") {
const file = output;
report({
text: background
? `Creating backup (${file.path})`
: `Saving file ${file.path}`
});
controller.enqueue({
path: file.path,
data: encoder.encode(file.data)
});
} else if (output.type === "attachment") {
report({
text: background
? `Creating backup (${output.hash})`
: `Saving attachment ${output.hash}`,
total: output.total,
current: output.current
});
const handle = await streamablefs.readFile(output.hash);
if (!handle) continue;
controller.enqueue({
path: output.path,
data: handle.readable
});
}
controller.close();
}
})
.pipeThrough(createZipStream())
.pipeTo(writeStream);
} finally {
updateAttachmentProgressEvent.unsubscribe();
}
controller.close();
}
})
.pipeThrough(createZipStream())
.pipeTo(writeStream);
}
});
if (error) {

View File

@@ -96,30 +96,27 @@ export async function saveContent(
ignoreEdit,
length: content.length
});
await Promise.race([
useEditorStore.getState().saveSessionContent(noteId, ignoreEdit, {
await useEditorStore
.getState()
.saveSessionContent(noteId, ignoreEdit, {
type: "tiptap",
data: content
}),
new Promise((_, reject) =>
setTimeout(
() => reject(new Error(strings.savingNoteTakingTooLong())),
30 * 1000
)
)
]).catch((e) => {
const { hide } = showToast(
"error",
(e as Error).message,
[
{
text: strings.dismiss(),
onClick: () => hide()
}
],
0
);
});
})
.catch((e) => {
const { hide } = showToast(
"error",
(e as Error).message,
[
{
text: strings.dismiss(),
onClick: () => hide()
}
],
0
);
});
}
const deferredSave = debounceWithId(saveContent, 100);

View File

@@ -58,7 +58,6 @@ export const ProgressDialog = DialogManager.register(function ProgressDialog<T>(
title={props.title}
description={props.subtitle}
onClose={() => {}}
width={500}
>
<Flex sx={{ flexDirection: "column" }}>
<Text variant="body">{text}</Text>

View File

@@ -174,10 +174,8 @@ function Recovery(props: RecoveryProps) {
<Text
sx={{
display: "flex",
alignSelf: "center",
alignItems: "center",
wordWrap: "break-word",
wordBreak: "break-all"
alignSelf: "end",
alignItems: "center"
}}
variant={"body"}
>
@@ -189,9 +187,7 @@ function Recovery(props: RecoveryProps) {
mt: 0,
ml: 2,
alignSelf: "start",
alignItems: "center",
textWrap: "wrap",
textAlign: "right"
alignItems: "center"
}}
variant={"secondary"}
onClick={() => openURL("/login")}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web-clipper",
"version": "0.4.1",
"version": "0.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web-clipper",
"version": "0.4.1",
"version": "0.4.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"@emotion/react": "11.11.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/web-clipper",
"version": "0.4.1",
"version": "0.4.0",
"private": true,
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",

View File

@@ -1,3 +0,0 @@
- Bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -182,6 +182,7 @@ export default class Vault {
async remove(noteId: string, password: string) {
await this.unlockNote(noteId, password, true);
if (!(await this.exists())) await this.create(password);
await this.db.relations.to({ id: noteId, type: "note" }, "vault").unlink();
}
@@ -202,11 +203,9 @@ export default class Vault {
);
if (password) {
try {
await this.unlock(password);
} catch {}
this.password = password;
if (!(await this.exists())) await this.create(password);
}
return { ...note, content };
}

View File

@@ -38,7 +38,6 @@ export class NNMigrationProvider implements MigrationProvider {
async up(db) {
await db.schema
.createTable("kv")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.addColumn("key", "text", (c) => c.primaryKey().unique().notNull())
.addColumn("value", "text")
@@ -47,7 +46,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("notes")
.ifNotExists()
// .modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.$call(addTrashColumns)
@@ -70,7 +68,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("content")
.ifNotExists()
// .modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("noteId", "text")
@@ -91,7 +88,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("notehistory")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("noteId", "text")
@@ -102,7 +98,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("sessioncontent")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("data", "text")
@@ -114,7 +109,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("notebooks")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.$call(addTrashColumns)
@@ -126,7 +120,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("tags")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("title", "text", COLLATE_NOCASE)
@@ -134,7 +127,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("colors")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("title", "text", COLLATE_NOCASE)
@@ -143,7 +135,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("vaults")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("title", "text", COLLATE_NOCASE)
@@ -152,7 +143,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("relations")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("fromType", "text")
@@ -163,7 +153,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("shortcuts")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("sortIndex", "integer")
@@ -173,7 +162,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("reminders")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("title", "text", COLLATE_NOCASE)
@@ -190,7 +178,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("attachments")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("iv", "text")
@@ -210,7 +197,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createTable("settings")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.$call(addBaseColumns)
.addColumn("key", "text", (c) => c.unique())
@@ -219,14 +205,12 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createIndex("notehistory_noteid")
.ifNotExists()
.on("notehistory")
.column("noteId")
.execute();
await db.schema
.createIndex("relation_from_general")
.ifNotExists()
.on("relations")
.columns(["fromType", "toType", "fromId"])
.where("toType", "!=", "note")
@@ -235,7 +219,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createIndex("relation_to_general")
.ifNotExists()
.on("relations")
.columns(["fromType", "toType", "toId"])
.where("fromType", "!=", "note")
@@ -244,7 +227,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createIndex("relation_from_note_notebook")
.ifNotExists()
.on("relations")
.columns(["fromType", "toType", "fromId", "toId"])
.where((eb) =>
@@ -257,7 +239,6 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createIndex("relation_to_note_notebook")
.ifNotExists()
.on("relations")
.columns(["fromType", "toType", "toId", "fromId"])
.where((eb) =>
@@ -270,42 +251,36 @@ export class NNMigrationProvider implements MigrationProvider {
await db.schema
.createIndex("note_type")
.ifNotExists()
.on("notes")
.columns(["type"])
.execute();
await db.schema
.createIndex("note_deleted")
.ifNotExists()
.on("notes")
.columns(["deleted"])
.execute();
await db.schema
.createIndex("note_date_deleted")
.ifNotExists()
.on("notes")
.columns(["dateDeleted"])
.execute();
await db.schema
.createIndex("notebook_type")
.ifNotExists()
.on("notebooks")
.columns(["type"])
.execute();
await db.schema
.createIndex("attachment_hash")
.ifNotExists()
.on("attachments")
.column("hash")
.execute();
await db.schema
.createIndex("content_noteId")
.ifNotExists()
.on("content")
.columns(["noteId"])
.execute();
@@ -341,7 +316,6 @@ export class NNMigrationProvider implements MigrationProvider {
async up(db) {
await db.schema
.createTable("config")
.ifNotExists()
.modifyEnd(sql`without rowid`)
.addColumn("name", "text", (c) => c.primaryKey().unique().notNull())
.addColumn("value", "text")
@@ -423,7 +397,6 @@ export class NNMigrationProvider implements MigrationProvider {
async up(db) {
await db.schema
.createTable("monographs")
.ifNotExists()
.$call(addBaseColumns)
.addColumn("datePublished", "integer")
.addColumn("title", "text", COLLATE_NOCASE)
@@ -440,7 +413,6 @@ export class NNMigrationProvider implements MigrationProvider {
.execute();
await db.schema
.createIndex("note_expiry_date")
.ifNotExists()
.on("notes")
.expression(sql`expiryDate ->> '$.value'`)
.execute();
@@ -513,9 +485,7 @@ function createFTS5Table(
sql.join(_options.map((o) => sql.raw(o)))
]);
return sql`CREATE VIRTUAL TABLE IF NOT EXISTS ${sql.raw(
name
)} USING fts5(${args})`;
return sql`CREATE VIRTUAL TABLE ${sql.raw(name)} USING fts5(${args})`;
}
async function runFTSTablesMigrations(db: Kysely<any>) {

View File

@@ -65,7 +65,6 @@ const Tiptap = ({
const tab = useTabContext();
const isFocused = useTabStore((state) => state.currentTab === tab?.id);
const [tick, setTick] = useState(0);
const [passwordError, setPasswordError] = useState<string | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
const noteStateUpdateTimer = useRef<NodeJS.Timeout>();
const tabRef = useRef<TabItem>(tab);
@@ -85,9 +84,6 @@ const Tiptap = ({
undo,
redo
};
useEffect(() => {
setPasswordError(null);
}, [tab.session?.noteId]);
logger("info", tabRef.current.id, "rendering");
@@ -664,31 +660,15 @@ const Tiptap = ({
</p>
<form
onSubmit={async (e) => {
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
const password = data.get("password");
if (!password) {
setPasswordError(strings.enterPassword());
return;
}
setPasswordError(null);
const biometrics = data.get("enrollBiometrics");
const result = await postAsyncWithTimeout(
EditorEvents.unlock,
{
password,
biometrics: biometrics === "on"
}
);
if (result && !result.success) {
setPasswordError(
result.error || strings.passwordIncorrect()
);
}
post("editor-events:unlock", {
password,
biometrics: biometrics === "on" ? true : false
});
}}
style={{
display: "flex",
@@ -703,9 +683,7 @@ const Tiptap = ({
ref={controller.passwordInputRef}
name="password"
type="password"
onChange={() => {
if (passwordError) setPasswordError(null);
}}
required
style={{
boxSizing: "border-box",
width: 300,
@@ -720,19 +698,6 @@ const Tiptap = ({
color: colors.primary.paragraph
}}
/>
{passwordError ? (
<p
style={{
color: colors.error.paragraph,
fontSize: "0.8rem",
margin: 0,
width: 300,
userSelect: "none"
}}
>
{passwordError}
</p>
) : null}
<button
className="unlock-note"

View File

@@ -2783,10 +2783,6 @@ msgstr "Expiry date cannot be more than 1 year in the future"
msgid "Expiry date must be in the future"
msgstr "Expiry date must be in the future"
#: src/strings.ts:2706
msgid "Expiry date removed"
msgstr "Expiry date removed"
#: src/strings.ts:2693
msgid "Expiry date set"
msgstr "Expiry date set"

View File

@@ -2772,10 +2772,6 @@ msgstr ""
msgid "Expiry date must be in the future"
msgstr ""
#: src/strings.ts:2706
msgid "Expiry date removed"
msgstr ""
#: src/strings.ts:2693
msgid "Expiry date set"
msgstr ""

View File

@@ -2702,6 +2702,5 @@ Continue without attachments?`,
t`We couldn't load this theme. The file appears to be incomplete or missing required theme properties.`,
copyLogs: () => t`Copy logs`,
permissionRequiredToSaveQRCode: () =>
t`Permission required to save QR-Code to Gallery`,
expiryDateRemoved: () => t`Expiry date removed`
t`Permission required to save QR-Code to Gallery`
};