diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 935ea05090..4f5473af38 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1491,7 +1491,11 @@
"new_password": "New password",
"new_password_confirmation": "New password confirmation",
"change_password": "Change password",
- "protected_session_timeout": "Protected Session Timeout",
+ "change_password_description": "Update your current password",
+ "reset_password": "Reset password",
+ "reset_password_description": "Permanently lose access to protected notes",
+ "cancel": "Cancel",
+ "protected_session_timeout": "Protected Session",
"protected_session_timeout_description": "Time of inactivity before the session is cleared from browser memory. See",
"wiki": "wiki",
"for_more_info": "for more info.",
diff --git a/apps/client/src/widgets/type_widgets/options/password.tsx b/apps/client/src/widgets/type_widgets/options/password.tsx
index ce12f52c8e..7e9dd3f966 100644
--- a/apps/client/src/widgets/type_widgets/options/password.tsx
+++ b/apps/client/src/widgets/type_widgets/options/password.tsx
@@ -1,18 +1,18 @@
-import { useState } from "preact/hooks"
-import { t } from "../../../services/i18n"
-import server from "../../../services/server"
-import toast from "../../../services/toast"
-import Alert from "../../react/Alert"
-import Button from "../../react/Button"
-import FormGroup from "../../react/FormGroup"
-import FormTextBox from "../../react/FormTextBox"
-import LinkButton from "../../react/LinkButton"
-import OptionsSection from "./components/OptionsSection"
-import OptionsRow from "./components/OptionsRow"
-import protected_session_holder from "../../../services/protected_session_holder"
-import { ChangePasswordResponse } from "@triliumnext/commons"
-import dialog from "../../../services/dialog"
-import TimeSelector from "./components/TimeSelector"
+import { useState } from "preact/hooks";
+import { createPortal } from "preact/compat";
+import { t } from "../../../services/i18n";
+import server from "../../../services/server";
+import toast from "../../../services/toast";
+import Button from "../../react/Button";
+import FormGroup from "../../react/FormGroup";
+import FormTextBox from "../../react/FormTextBox";
+import OptionsSection from "./components/OptionsSection";
+import OptionsRow, { OptionsRowWithButton } from "./components/OptionsRow";
+import protected_session_holder from "../../../services/protected_session_holder";
+import { ChangePasswordResponse } from "@triliumnext/commons";
+import dialog from "../../../services/dialog";
+import TimeSelector from "./components/TimeSelector";
+import Modal from "../../react/Modal";
export default function PasswordSettings() {
return (
@@ -20,86 +20,125 @@ export default function PasswordSettings() {
>
- )
+ );
}
function ChangePassword() {
- const [ oldPassword, setOldPassword ] = useState("");
- const [ newPassword1, setNewPassword1 ] = useState("");
- const [ newPassword2, setNewPassword2 ] = useState("");
+ const [showModal, setShowModal] = useState(false);
return (
-
- {t("password.alert_message")}
-
- {
- if (!confirm(t("password.reset_confirmation"))) {
- return;
- }
+ setShowModal(true)}
+ />
- await server.post("password/reset?really=yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes");
- toast.showError(t("password.reset_success_message"));
- }}
- />
-
+ {
+ if (!await dialog.confirm(t("password.reset_confirmation"))) {
+ return;
+ }
-
+ {createPortal(
+ setShowModal(false)} />,
+ document.body
+ )}
- )
+ );
+}
+
+interface ChangePasswordModalProps {
+ show: boolean;
+ onHidden: () => void;
+}
+
+function ChangePasswordModal({ show, onHidden }: ChangePasswordModalProps) {
+ const [oldPassword, setOldPassword] = useState("");
+ const [newPassword1, setNewPassword1] = useState("");
+ const [newPassword2, setNewPassword2] = useState("");
+
+ const handleSubmit = async () => {
+ if (newPassword1 !== newPassword2) {
+ toast.showError(t("password.password_mismatch"));
+ return;
+ }
+
+ const result = await server.post("password/change", {
+ current_password: oldPassword,
+ new_password: newPassword1
+ });
+
+ if (result.success) {
+ onHidden();
+ setOldPassword("");
+ setNewPassword1("");
+ setNewPassword2("");
+ await dialog.info(t("password.password_changed_success"));
+
+ // password changed so current protected session is invalid and needs to be cleared
+ protected_session_holder.resetProtectedSession();
+ } else if (result.message) {
+ toast.showError(result.message);
+ }
+ };
+
+ const handleHidden = () => {
+ setOldPassword("");
+ setNewPassword1("");
+ setNewPassword2("");
+ onHidden();
+ };
+
+ return (
+
+
+
+ >
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ );
}
function ProtectedSessionTimeout() {
@@ -112,10 +151,11 @@ function ProtectedSessionTimeout() {
>
- )
-}
\ No newline at end of file
+ );
+}