mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 11:39:21 +02:00
Compare commits
1 Commits
web/backup
...
fix/remove
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a6032d4f4 |
@@ -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')
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -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);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -721,10 +721,7 @@ export const useEditorEvents = (
|
||||
}
|
||||
|
||||
case EditorEvents.unlock: {
|
||||
eSendEvent(eUnlockWithPassword, {
|
||||
...editorMessage.value,
|
||||
resolverId: editorMessage.resolverId
|
||||
});
|
||||
eSendEvent(eUnlockWithPassword, editorMessage.value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
4
apps/mobile/package-lock.json
generated
4
apps/mobile/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.27",
|
||||
"version": "3.3.25",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")}
|
||||
|
||||
4
extensions/web-clipper/package-lock.json
generated
4
extensions/web-clipper/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
- Bug fixes and improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user