mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 08:56:52 +00:00
refactor: extract welcome modal for consistency
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import {
|
||||
HandPalmIcon,
|
||||
ShieldCheckIcon,
|
||||
WarningIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import Logo from "../Logo.tsx";
|
||||
import { Modal } from "../ui/Modal";
|
||||
import Saajan from "../ui/Saajan.tsx";
|
||||
|
||||
export default function WelcomeModal({
|
||||
setShowWelcome,
|
||||
}: {
|
||||
setShowWelcome: (show: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Modal isOpen={true}>
|
||||
<div className="flex flex-col items-center text-center gap-4">
|
||||
<div className="bg-primary/10 p-4 rounded-full animate-pulse">
|
||||
<ShieldCheckIcon
|
||||
size={48}
|
||||
weight="duotone"
|
||||
className="text-primary"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="font-display text-2xl font-bold text-primary">
|
||||
Welcome to
|
||||
<Logo /> !
|
||||
</h3>
|
||||
<p className="text-base-content/80 leading-relaxed">
|
||||
Before we begin, let me make a small promise.
|
||||
<HandPalmIcon
|
||||
size={18}
|
||||
className="inline text-primary"
|
||||
weight="fill"
|
||||
/>
|
||||
<div className="divider my-0"></div>
|
||||
<br />
|
||||
Everything you write here is sealed with your password,{" "}
|
||||
<span className="font-display text-success">cryptographically</span>
|
||||
, before it leaves your hands.
|
||||
<br />A fancy way of saying, I couldn't if I tried.
|
||||
</p>
|
||||
|
||||
<div className="alert alert-warning bg-paper/20 border-paper/20 flex items-start gap-3 text-left py-3">
|
||||
<WarningIcon size={24} weight="fill" className="shrink-0 mt-0.5" />
|
||||
<p className="text-sm font-medium text-primary-content">
|
||||
If you ever happen to forget your password, your letters are lost
|
||||
to time, forever.
|
||||
<br />
|
||||
<span className="font-bold mt-2">
|
||||
I highly, highly recommend storing this password in your{" "}
|
||||
<a
|
||||
href="https://www.privacyguides.org/en/passwords/"
|
||||
target="_blank"
|
||||
className="link link-primary-content"
|
||||
rel="noopener"
|
||||
>
|
||||
password manager
|
||||
</a>{" "}
|
||||
or somewhere safe to remember it.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="modal-action w-full">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowWelcome(false)}
|
||||
className="btn btn-primary w-full shadow-lg"
|
||||
>
|
||||
I'll remember
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<div className="absolute bottom-0 z-1000 font-sans w-full">
|
||||
<Saajan
|
||||
position="top"
|
||||
message={"I've lost words before.\nI know what it feels like."}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -25,14 +25,18 @@ export const LogModal = ({
|
||||
<WarningIcon className="text-warning" size={16} weight="duotone" />
|
||||
)}
|
||||
{message}
|
||||
<div className="divider text-primary-content text-xs uppercase tracking-widest">
|
||||
Error Stack
|
||||
</div>
|
||||
<div className="mockup-code bg-base-100 text-error w-full">
|
||||
<pre>
|
||||
<code>{String(log)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
{log && (
|
||||
<>
|
||||
<div className="divider text-primary-content text-xs uppercase tracking-widest">
|
||||
Error Stack
|
||||
</div>
|
||||
<div className="mockup-code bg-base-100 text-error w-full">
|
||||
<pre>
|
||||
<code>{String(log)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
HandPalmIcon,
|
||||
ShieldCheckIcon,
|
||||
WarningIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -11,8 +7,8 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { z } from "zod";
|
||||
import { api, publicApi } from "../api/apiClient";
|
||||
import Logo from "../components/Logo";
|
||||
import WelcomeModal from "../components/login/WelcomeModal.tsx";
|
||||
import FormField from "../components/ui/FormField";
|
||||
import { Modal } from "../components/ui/Modal";
|
||||
import Saajan from "../components/ui/Saajan";
|
||||
import { endpoints } from "../config/endpoints";
|
||||
import { ROUTES } from "../config/routes";
|
||||
@@ -26,83 +22,6 @@ const loginSchema = z.object({
|
||||
|
||||
type LoginInputs = z.infer<typeof loginSchema>;
|
||||
|
||||
function WelcomeModal({
|
||||
setShowWelcome,
|
||||
}: {
|
||||
setShowWelcome: (show: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Modal isOpen={true}>
|
||||
<div className="flex flex-col items-center text-center gap-4">
|
||||
<div className="bg-primary/10 p-4 rounded-full animate-pulse">
|
||||
<ShieldCheckIcon
|
||||
size={48}
|
||||
weight="duotone"
|
||||
className="text-primary"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="font-display text-2xl font-bold text-primary">
|
||||
Welcome to
|
||||
<Logo /> !
|
||||
</h3>
|
||||
<p className="text-base-content/80 leading-relaxed">
|
||||
Before we begin, let me make a small promise.
|
||||
<HandPalmIcon
|
||||
size={18}
|
||||
className="inline text-primary"
|
||||
weight="fill"
|
||||
/>
|
||||
<div className="divider my-0"></div>
|
||||
<br />
|
||||
Everything you write here is sealed with your password,{" "}
|
||||
<span className="font-display text-success">cryptographically</span>
|
||||
, before it leaves your hands.
|
||||
<br />A fancy way of saying, I couldn't if I tried.
|
||||
</p>
|
||||
|
||||
<div className="alert alert-warning bg-paper/20 border-paper/20 flex items-start gap-3 text-left py-3">
|
||||
<WarningIcon size={24} weight="fill" className="shrink-0 mt-0.5" />
|
||||
<p className="text-sm font-medium text-primary-content">
|
||||
If you ever happen to forget your password, your letters are lost
|
||||
to time, forever.
|
||||
<br />
|
||||
<span className="font-bold mt-2">
|
||||
I highly, highly recommend storing this password in your{" "}
|
||||
<a
|
||||
href="https://www.privacyguides.org/en/passwords/"
|
||||
target="_blank"
|
||||
className="link link-primary-content"
|
||||
rel="noopener"
|
||||
>
|
||||
password manager
|
||||
</a>{" "}
|
||||
or somewhere safe to remember it.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="modal-action w-full">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowWelcome(false)}
|
||||
className="btn btn-primary w-full shadow-lg"
|
||||
>
|
||||
I'll remember
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<div className="absolute bottom-0 z-1000 font-sans w-full">
|
||||
<Saajan
|
||||
position="top"
|
||||
message={"I've lost words before.\nI know what it feels like."}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -127,7 +46,7 @@ export default function Login() {
|
||||
setIsLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
// client side key derivation for 0 knowledge
|
||||
// client side key derivation for e2e encryption
|
||||
const { masterKey, authHash } = await CryptoUtils.deriveKeyBundle(
|
||||
data.password,
|
||||
data.email,
|
||||
@@ -143,7 +62,6 @@ export default function Login() {
|
||||
headers: { Authorization: `Bearer ${authData.access}` },
|
||||
});
|
||||
|
||||
// store the auth related data
|
||||
await setAuthStore(authData.access, userData, masterKey);
|
||||
|
||||
navigate(nextRoute, { replace: true });
|
||||
|
||||
@@ -13,7 +13,6 @@ import { endpoints } from "../config/endpoints";
|
||||
import { ROUTES } from "../config/routes";
|
||||
import { CryptoUtils } from "../utils/crypto";
|
||||
|
||||
// validation logic
|
||||
const registerSchema = z
|
||||
.object({
|
||||
full_name: z.string().min(2, "Name must be at least 2 characters"),
|
||||
@@ -49,7 +48,7 @@ export default function Register() {
|
||||
setIsLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
// We generate the key bundle here to get the authHash (password) for the server.
|
||||
// we generate the key bundle here to get the authHash (password) to be haSHed and stored in the db.
|
||||
const { authHash } = await CryptoUtils.deriveKeyBundle(
|
||||
data.password,
|
||||
data.email,
|
||||
@@ -136,7 +135,6 @@ export default function Register() {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Warning */}
|
||||
<div className="alert alert-warning items-start text-left p-3 gap-2 rounded-md border-warning/20">
|
||||
<InfoIcon size={20} weight="duotone" className="mt-0.5 shrink-0" />
|
||||
<p className="text-sm font-semibold">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { openDB } from "idb";
|
||||
|
||||
// we use this to store master key in browser - secure and good UX
|
||||
// we use indexedDB to securely store master key for easier access across tabs (better UX than having to store in session)
|
||||
const db = openDB("piku-keys", 1, {
|
||||
upgrade(db) {
|
||||
db.createObjectStore("master-key");
|
||||
|
||||
Reference in New Issue
Block a user