From c4f1ff92c9ec37a321d621b6db5c9d7cd3ebffdc Mon Sep 17 00:00:00 2001 From: Codesamp-Rohan <22ebkcs104.rohan.chaudhary@bkbiet.ac.in> Date: Fri, 27 Feb 2026 19:58:47 +0530 Subject: [PATCH] updated UI --- dev.js | 32 +- index.js | 22 +- package-lock.json | 8 + src/App.css | 4 +- src/App.jsx | 553 +++++++++--------- src/components/layout/Overlay.jsx | 28 +- src/components/layout/SideBar.jsx | 381 ++++++------ src/components/menu/ShortMenu.jsx | 66 +-- src/components/notes/NoteCard.jsx | 72 +-- src/components/notes/NoteSideBar.jsx | 194 +++--- src/components/password/PasswordCard.jsx | 88 +-- src/components/password/PasswordInput.jsx | 80 +-- src/components/password/Search.jsx | 48 +- src/components/popups/Alert.jsx | 48 +- src/components/popups/DecryptPopUp.jsx | 58 +- src/components/ui/Button.jsx | 18 +- src/components/ui/Divider.jsx | 12 +- src/components/ui/Input.jsx | 16 +- src/components/ui/Loader.jsx | 16 +- src/components/ui/Tag.jsx | 10 +- src/components/ui/ToggleSwitch.jsx | 34 +- src/features/notes/AddNoteForm.jsx | 228 ++++---- src/features/notes/NoteManager.jsx | 192 +++--- src/features/passwords/AddPasswordForm.jsx | 381 ++++++------ .../passwords/GeneratePasswordForm.jsx | 166 +++--- src/features/passwords/PasswordManager.jsx | 278 ++++----- src/features/setup/SetupVault.jsx | 148 ++--- src/lib/crpyto.js | 84 +-- src/lib/generatePassword.js | 36 +- src/lib/vaultPass.js | 180 +++--- src/store/globalStore.js | 12 +- src/store/optionStore.js | 8 +- src/utils/timeAgo.js | 28 +- 33 files changed, 1775 insertions(+), 1754 deletions(-) diff --git a/dev.js b/dev.js index 59a0a5a..6fc68f7 100644 --- a/dev.js +++ b/dev.js @@ -1,21 +1,21 @@ -import updates from "pear-updates"; -import { spawn } from "bare-subprocess"; -import { isWindows } from "."; +import updates from 'pear-updates' +import { spawn } from 'bare-subprocess' +import { isWindows } from '.' isWindows - ? spawn("npm.cmd", ["run", "build"], { stdio: "inherit" }) - : spawn("npm", ["run", "build"], { stdio: "inherit" }); + ? spawn('npm.cmd', ['run', 'build'], { stdio: 'inherit' }) + : spawn('npm', ['run', 'build'], { stdio: 'inherit' }) -const REGULATE = 500; -let throttle = Date.now() + REGULATE; +const REGULATE = 500 +let throttle = Date.now() + REGULATE updates({ app: true, version: { key: null } }, (update) => { - if (Date.now() < throttle) return; - throttle = Infinity; - console.log("Update", update); + if (Date.now() < throttle) return + throttle = Infinity + console.log('Update', update) const build = isWindows - ? spawn("npm.cmd", ["run", "build"], { stdio: "inherit" }) - : spawn("npm", ["run", "build"], { stdio: "inherit" }); - build.on("close", () => { - throttle = Date.now() + REGULATE; - }); -}); + ? spawn('npm.cmd', ['run', 'build'], { stdio: 'inherit' }) + : spawn('npm', ['run', 'build'], { stdio: 'inherit' }) + build.on('close', () => { + throttle = Date.now() + REGULATE + }) +}) diff --git a/index.js b/index.js index 68ad6a8..16aa26b 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,17 @@ /* global Pear */ -import Runtime from "pear-electron"; -import Bridge from "pear-bridge"; -import process from "bare-process"; +import Runtime from 'pear-electron' +import Bridge from 'pear-bridge' +import process from 'bare-process' -const onDisk = Pear.app.key === null; -if (onDisk) await import("./dev.js"); +const onDisk = Pear.app.key === null +if (onDisk) await import('./dev.js') -const bridge = new Bridge(); -await bridge.ready(); -const runtime = new Runtime(); +const bridge = new Bridge() +await bridge.ready() +const runtime = new Runtime() -const pipe = await runtime.start({ bridge }); +const pipe = await runtime.start({ bridge }) -pipe.on("end", () => Pear.exit()); +pipe.on('end', () => Pear.exit()) -export const isWindows = process.platform === "win32"; +export const isWindows = process.platform === 'win32' diff --git a/package-lock.json b/package-lock.json index 300ff9f..0df3530 100644 --- a/package-lock.json +++ b/package-lock.json @@ -640,6 +640,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1298,6 +1299,7 @@ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "bare-path": "^3.0.0" } @@ -2131,6 +2133,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2328,6 +2331,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -2382,6 +2386,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "license": "MIT", + "peer": true, "dependencies": { "builtins": "^5.0.1", "eslint-plugin-es": "^4.1.0", @@ -2419,6 +2424,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "license": "ISC", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2434,6 +2440,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -4966,6 +4973,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/src/App.css b/src/App.css index 84b2a7a..5d656af 100644 --- a/src/App.css +++ b/src/App.css @@ -764,7 +764,7 @@ textarea:focus { display: flex; flex-direction: column; gap: 0.5rem; - padding: 0.5rem 0.25rem 0.5rem 0.5rem; + padding: 0.5rem 0.75rem 0.5rem 0.5rem; overflow: auto; flex: 1; } @@ -964,7 +964,7 @@ textarea:focus { background: var(--bg-secondary); box-shadow: 2px 2px 0 var(--border); min-width: 140px; - z-index: 1001; + z-index: 999; display: flex; flex-direction: column; padding: 0.25rem; diff --git a/src/App.jsx b/src/App.jsx index b69a006..71f9873 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,40 +1,40 @@ /* global alert */ -import React from "react"; -import { createRoot } from "react-dom/client"; -import { SideBar } from "@/src/components/layout/SideBar.jsx"; -import ToggleSwitch from "@/src/components/ui/ToggleSwitch.jsx"; -import { DecryptPopUp } from "@/src/components/popups/DecryptPopUp.jsx"; -import { Overlay } from "@/src/components/layout/Overlay.jsx"; -import Alert from "@/src/components/popups/Alert.jsx"; -import { PasswordManager } from "@/src/features/passwords/PasswordManager.jsx"; -import { NoteManager } from "@/src/features/notes/NoteManager.jsx"; -import { AddPasswordForm } from "@/src/features/passwords/AddPasswordForm"; -import { AddNoteForm } from "@/src/features/notes/AddNoteForm"; -import { GeneratePasswordForm } from "@/src/features/passwords/GeneratePasswordForm.jsx"; -import { SetupVault } from "@/src/features/setup/SetupVault.jsx"; -import { useOptionStore } from "@/src/store/optionStore.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import { useShallow } from "zustand/react/shallow"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { FaCopy } from "react-icons/fa"; -import { BiExit, BiExport } from "react-icons/bi"; -import { IoFlash } from "react-icons/io5"; -import { FiKey, FiPlus, FiRefreshCcw, FiShare, FiUpload } from "react-icons/fi"; -import "@/src/App.css"; -import { Loader } from "./components/ui/Loader"; -import { encryptString, decryptString } from "./lib/crpyto"; +import React from 'react' +import { createRoot } from 'react-dom/client' +import { SideBar } from '@/src/components/layout/SideBar.jsx' +import ToggleSwitch from '@/src/components/ui/ToggleSwitch.jsx' +import { DecryptPopUp } from '@/src/components/popups/DecryptPopUp.jsx' +import { Overlay } from '@/src/components/layout/Overlay.jsx' +import Alert from '@/src/components/popups/Alert.jsx' +import { PasswordManager } from '@/src/features/passwords/PasswordManager.jsx' +import { NoteManager } from '@/src/features/notes/NoteManager.jsx' +import { AddPasswordForm } from '@/src/features/passwords/AddPasswordForm' +import { AddNoteForm } from '@/src/features/notes/AddNoteForm' +import { GeneratePasswordForm } from '@/src/features/passwords/GeneratePasswordForm.jsx' +import { SetupVault } from '@/src/features/setup/SetupVault.jsx' +import { useOptionStore } from '@/src/store/optionStore.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import { useShallow } from 'zustand/react/shallow' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { FaCopy } from 'react-icons/fa' +import { BiExit, BiExport } from 'react-icons/bi' +import { IoFlash } from 'react-icons/io5' +import { FiKey, FiPlus, FiRefreshCcw, FiShare, FiUpload } from 'react-icons/fi' +import '@/src/App.css' +import { Loader } from './components/ui/Loader' +import { encryptString, decryptString } from './lib/crpyto' const bar = { - WebkitAppRegion: "drag", - position: "fixed", - height: "1.8em", - width: "100%", - backgroundColor: "#fefff4", + WebkitAppRegion: 'drag', + position: 'fixed', + height: '1.8em', + width: '100%', + backgroundColor: '#fefff4', zIndex: 1000, - borderBottom: "1px solid var(--border)", -}; + borderBottom: '1px solid var(--border)' +} -function App() { +function App () { const { vaultPassInitialized, currentOS, @@ -46,7 +46,7 @@ function App() { toggleNoteForm, isLoading, vaultPassInv, - setAllVaultEntries, + setAllVaultEntries } = useGlobalStore( useShallow((state) => ({ vaultPassInitialized: state.vaultPassInitialized, @@ -59,328 +59,331 @@ function App() { toggleNoteForm: state.toggleNoteForm, isLoading: state.isLoading, vaultPassInv: state.vaultPassInv, - setAllVaultEntries: state.setAllVaultEntries, - })), - ); + setAllVaultEntries: state.setAllVaultEntries + })) + ) - const [passwords, setPasswords] = React.useState([]); - const [selectedPassword, setSelectedPassword] = React.useState(null); - const [showSuccessAlert, setShowSuccessAlert] = React.useState(false); - const [isShareMenuOpen, setIsShareMenuOpen] = React.useState(false); - const [showDecryptPopup, setShowDecryptPopup] = React.useState(false); - const [encryptedFileContent, setEncryptedFileContent] = React.useState(""); - const [roomKey, setRoomKey] = React.useState(""); - const [decryptError, setDecryptError] = React.useState(""); - const fileInputRef = React.useRef(null); - const option = useOptionStore((state) => state.mode); + const [passwords, setPasswords] = React.useState([]) + const [selectedPassword, setSelectedPassword] = React.useState(null) + const [showSuccessAlert, setShowSuccessAlert] = React.useState(false) + const [isShareMenuOpen, setIsShareMenuOpen] = React.useState(false) + const [showDecryptPopup, setShowDecryptPopup] = React.useState(false) + const [encryptedFileContent, setEncryptedFileContent] = React.useState('') + const [roomKey, setRoomKey] = React.useState('') + const [decryptError, setDecryptError] = React.useState('') + const fileInputRef = React.useRef(null) + const option = useOptionStore((state) => state.mode) React.useEffect(() => { const boot = async () => { - const exists = await vaultPass.vaultExists(); + const exists = await vaultPass.vaultExists() if (exists) { - await vaultPass.init(); - const savedInvCode = await vaultPass.loadInviteCode(); + await vaultPass.init() + const savedInvCode = await vaultPass.loadInviteCode() if (savedInvCode) { - useGlobalStore.getState().setVaultPassInv(savedInvCode); + useGlobalStore.getState().setVaultPassInv(savedInvCode) } - useGlobalStore.getState().setVaultPassInitialized(true); + useGlobalStore.getState().setVaultPassInitialized(true) } else { - useGlobalStore.getState().setVaultPassInitialized(false); - useGlobalStore.getState().setVaultPassInv(null); + useGlobalStore.getState().setVaultPassInitialized(false) + useGlobalStore.getState().setVaultPassInv(null) } - }; + } - boot(); - }, []); + boot() + }, []) const handleExportVaultDataset = async () => { try { - const allEntries = await vaultPass.getAll(); + const allEntries = await vaultPass.getAll() const encryptedEntries = await encryptString( JSON.stringify(allEntries), - vaultPassInv, - ); - setAllVaultEntries(encryptedEntries); + vaultPassInv + ) + setAllVaultEntries(encryptedEntries) const dataStr = - "data:text/plain;charset=utf-8," + encodeURIComponent(encryptedEntries); - const downloadAnchorNode = document.createElement("a"); - downloadAnchorNode.setAttribute("href", dataStr); - downloadAnchorNode.setAttribute("download", "vault_entries.txt"); - document.body.appendChild(downloadAnchorNode); - downloadAnchorNode.click(); - document.body.removeChild(downloadAnchorNode); - setIsShareMenuOpen(false); + 'data:text/plain;charset=utf-8,' + encodeURIComponent(encryptedEntries) + const downloadAnchorNode = document.createElement('a') + downloadAnchorNode.setAttribute('href', dataStr) + downloadAnchorNode.setAttribute('download', 'vault_entries.txt') + document.body.appendChild(downloadAnchorNode) + downloadAnchorNode.click() + document.body.removeChild(downloadAnchorNode) + setIsShareMenuOpen(false) } catch (e) { - console.error("Error exporting dataset:", e); - alert("Failed to export dataset"); + console.error('Error exporting dataset:', e) + alert('Failed to export dataset') } - }; + } const handleUploadVaultDataset = async (event) => { try { - const file = event.target.files?.[0]; - if (!file) return; + const file = event.target.files?.[0] + if (!file) return - const text = await file.text(); - setEncryptedFileContent(text); - setShowDecryptPopup(true); - setIsShareMenuOpen(false); + const text = await file.text() + setEncryptedFileContent(text) + setShowDecryptPopup(true) + setIsShareMenuOpen(false) } catch (e) { - console.error("Error reading file:", e); - alert("Failed to read file"); + console.error('Error reading file:', e) + alert('Failed to read file') } finally { - if (event.target) event.target.value = ""; + if (event.target) event.target.value = '' } - }; + } const handleDecryptAndImport = async () => { if (!roomKey.trim()) { - setDecryptError("Please enter the room key"); - return; + setDecryptError('Please enter the room key') + return } try { - setDecryptError(""); + setDecryptError('') const decryptedText = await decryptString( encryptedFileContent, - roomKey.trim(), - ); - const entries = JSON.parse(decryptedText); + roomKey.trim() + ) + const entries = JSON.parse(decryptedText) if (!Array.isArray(entries)) { - alert("Invalid file format. Expected a JSON array."); - return; + alert('Invalid file format. Expected a JSON array.') + return } - const addedEntries = []; + const addedEntries = [] for (const entry of entries) { - const { id, createdAt, updatedAt, ...data } = entry; - await vaultPass.add(data); - addedEntries.push(data); + const { id, createdAt, updatedAt, ...data } = entry + await vaultPass.add(data) + addedEntries.push(data) } - useGlobalStore.getState().triggerRefreshPasswords(); - useGlobalStore.getState().triggerRefreshNotes(); - setShowDecryptPopup(false); - setEncryptedFileContent(""); - setRoomKey(""); + useGlobalStore.getState().triggerRefreshPasswords() + useGlobalStore.getState().triggerRefreshNotes() + setShowDecryptPopup(false) + setEncryptedFileContent('') + setRoomKey('') } catch (e) { - console.error("Error decrypting/importing dataset:", e); - setDecryptError("Invalid room key or corrupted file. Please try again."); + console.error('Error decrypting/importing dataset:', e) + setDecryptError('Invalid room key or corrupted file. Please try again.') } - }; + } const handleCancelDecrypt = () => { - setShowDecryptPopup(false); - setEncryptedFileContent(""); - setRoomKey(""); - setDecryptError(""); - }; + setShowDecryptPopup(false) + setEncryptedFileContent('') + setRoomKey('') + setDecryptError('') + } const handleExitVault = async () => { try { - await vaultPass.resetVault(); - useGlobalStore.getState().setVaultPassInitialized(false); - useGlobalStore.getState().setVaultPassInv(null); + await vaultPass.resetVault() + useGlobalStore.getState().setVaultPassInitialized(false) + useGlobalStore.getState().setVaultPassInv(null) } catch (e) { - console.error("Error resetting vault:", e); - alert("Failed to reset vault"); + console.error('Error resetting vault:', e) + alert('Failed to reset vault') } - }; + } const handleSelectPassword = (password) => { - setSelectedPassword(password); - }; + setSelectedPassword(password) + } const handleDeletePassword = async (id) => { try { - await vaultPass.delete(id); - setPasswords((prev) => prev.filter((pass) => pass.id !== id)); + await vaultPass.delete(id) + setPasswords((prev) => prev.filter((pass) => pass.id !== id)) } catch (e) { - console.error("Error deleting password:", e); + console.error('Error deleting password:', e) } - }; + } const handleCloseSidebar = () => { - setSelectedPassword(null); - }; + setSelectedPassword(null) + } const copyInvCode = async () => { try { - await navigator.clipboard.writeText(vaultPassInv); - setShowSuccessAlert(true); + await navigator.clipboard.writeText(vaultPassInv) + setShowSuccessAlert(true) } catch (e) { - console.error("Error copying invite code:", e); - alert("Failed to copy invite code"); + console.error('Error copying invite code:', e) + alert('Failed to copy invite code') } - }; + } const applicationReload = () => { - useGlobalStore.getState().setIsLoading(true); - window.location.reload(); - }; + useGlobalStore.getState().setIsLoading(true) + window.location.reload() + } if (!vaultPassInitialized || vaultPass === null) { - return ; + return } - return !vaultPassInitialized && vaultPass === null ? ( - - ) : ( -
- {showSuccessAlert && ( - } - title="Success!" - message="Invite code - copied to clipboard!" - onClose={() => setShowSuccessAlert(false)} - /> - )} -
- -

- VAULT -

- - - End-to-End Encrypted + return !vaultPassInitialized && vaultPass === null + ? ( + + ) + : ( +
+ {showSuccessAlert && ( + } + title='Success!' + message='Invite code + copied to clipboard!' + onClose={() => setShowSuccessAlert(false)} + /> + )} +
+ +

+ VAULT +

+ + + End-to-End Encrypted + -
- -
- {isLoading && } -
- + {isLoading && } +
+ + + {option === 'password' + ? ( + + ) + : ( + )} - + + + + )} + {isGenerateFormActive && ( + <> + + + + )} + {isNoteFormActive && ( + <> + + + + )} + {showDecryptPopup && ( + <> + + -
- - - - - + + )}
- - {option === "password" ? ( - - ) : ( - - )} - {isPasswordFormActive && ( - <> - - - - )} - {isGenerateFormActive && ( - <> - - - - )} - {isNoteFormActive && ( - <> - - - - )} - {showDecryptPopup && ( - <> - - - - )}
-
- ); + ) } -createRoot(document.getElementById("root")).render(); +createRoot(document.getElementById('root')).render() diff --git a/src/components/layout/Overlay.jsx b/src/components/layout/Overlay.jsx index fa07d13..6c63788 100644 --- a/src/components/layout/Overlay.jsx +++ b/src/components/layout/Overlay.jsx @@ -1,6 +1,6 @@ -import React from "react"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import { useShallow } from "zustand/react/shallow"; +import React from 'react' +import { useGlobalStore } from '@/src/store/globalStore.js' +import { useShallow } from 'zustand/react/shallow' export const Overlay = React.memo(() => { const { @@ -11,7 +11,7 @@ export const Overlay = React.memo(() => { isPasswordFormActive, isGenerateFormActive, isNoteFormActive, - isAllEnteriesPopupActive, + isAllEnteriesPopupActive } = useGlobalStore( useShallow((state) => ({ togglePasswordForm: state.togglePasswordForm, @@ -21,16 +21,16 @@ export const Overlay = React.memo(() => { isPasswordFormActive: state.isPasswordFormActive, isGenerateFormActive: state.isGenerateFormActive, isNoteFormActive: state.isNoteFormActive, - isAllEnteriesPopupActive: state.isAllEnteriesPopupActive, - })), - ); + isAllEnteriesPopupActive: state.isAllEnteriesPopupActive + })) + ) const handleOverlayClick = () => { - isPasswordFormActive && togglePasswordForm(); - isGenerateFormActive && toggleGenerateForm(); - isNoteFormActive && toggleNoteForm(); - isAllEnteriesPopupActive && toggleAllEnteriesPopup(); - }; + isPasswordFormActive && togglePasswordForm() + isGenerateFormActive && toggleGenerateForm() + isNoteFormActive && toggleNoteForm() + isAllEnteriesPopupActive && toggleAllEnteriesPopup() + } - return
; -}); + return
+}) diff --git a/src/components/layout/SideBar.jsx b/src/components/layout/SideBar.jsx index 383747b..eb61dac 100644 --- a/src/components/layout/SideBar.jsx +++ b/src/components/layout/SideBar.jsx @@ -1,264 +1,263 @@ /* global alert */ -import React, { useState } from "react"; -import { FiX, FiTrash2, FiEdit, FiSave } from "react-icons/fi"; -import { timeAgo } from "@/src/utils/timeAgo"; -import PasswordInput from "../password/PasswordInput.jsx"; -import { Input } from "@/src/components/ui/Input.jsx"; -import { Button } from "@/src/components/ui/Button.jsx"; -import { Tag } from "@/src/components/ui/Tag.jsx"; -import "@/src/App.css"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import Alert from "@/src/components/popups/Alert.jsx"; -import { FaCheckCircle } from "react-icons/fa"; +import React, { useState } from 'react' +import { FiX, FiTrash2, FiEdit, FiSave } from 'react-icons/fi' +import { timeAgo } from '@/src/utils/timeAgo' +import PasswordInput from '../password/PasswordInput.jsx' +import { Input } from '@/src/components/ui/Input.jsx' +import { Button } from '@/src/components/ui/Button.jsx' +import { Tag } from '@/src/components/ui/Tag.jsx' +import '@/src/App.css' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import Alert from '@/src/components/popups/Alert.jsx' +import { FaCheckCircle } from 'react-icons/fa' export const SideBar = ({ password, onClose, onDelete }) => { - const PREDEFINED_TAGS = ["work", "personal"]; - if (!password) return null; + const PREDEFINED_TAGS = ['work', 'personal'] + if (!password) return null - const normalizeTag = (tag) => tag.trim().toLowerCase(); + const normalizeTag = (tag) => tag.trim().toLowerCase() - const [isEditMode, setIsEditMode] = useState(false); - const [newName, setNewName] = useState(password.name); - const [newWebsite, setNewWebsite] = useState(password.website); - const [newUsername, setNewUsername] = useState(password.username); - const [newPassword, setNewPassword] = useState(password.password); + const [isEditMode, setIsEditMode] = useState(false) + const [newName, setNewName] = useState(password.name) + const [newWebsite, setNewWebsite] = useState(password.website) + const [newUsername, setNewUsername] = useState(password.username) + const [newPassword, setNewPassword] = useState(password.password) const [newTag, setNewTag] = useState( - password.tag ? normalizeTag(password.tag) : "", - ); - const [tagInput, setTagInput] = useState(""); - const [showTagSuggestions, setShowTagSuggestions] = useState(false); - const [customTags, setCustomTags] = useState([]); - const [showSuccessAlert, setShowSuccessAlert] = useState(false); + password.tag ? normalizeTag(password.tag) : '' + ) + const [tagInput, setTagInput] = useState('') + const [showTagSuggestions, setShowTagSuggestions] = useState(false) + const [customTags, setCustomTags] = useState([]) + const [showSuccessAlert, setShowSuccessAlert] = useState(false) React.useEffect(() => { const fetchExistingTags = async () => { try { - const entries = await vaultPass.getAll(); + const entries = await vaultPass.getAll() const allTags = entries .map((entry) => entry.tag) .filter((tag) => tag) .map(normalizeTag) .filter((tag) => tag && !PREDEFINED_TAGS.includes(tag)) - .filter((tag, index, self) => self.indexOf(tag) === index); - setCustomTags(allTags); + .filter((tag, index, self) => self.indexOf(tag) === index) + setCustomTags(allTags) } catch (error) { - console.error("Error fetching tags:", error); + console.error('Error fetching tags:', error) } - }; + } if (isEditMode) { - fetchExistingTags(); + fetchExistingTags() } - }, [isEditMode]); + }, [isEditMode]) const handleTagInput = (value) => { - setTagInput(value); - if (value.startsWith("/")) { - setShowTagSuggestions(true); + setTagInput(value) + if (value.startsWith('/')) { + setShowTagSuggestions(true) } else { - setShowTagSuggestions(false); + setShowTagSuggestions(false) } - }; + } const handleSelectTag = (tag) => { - setNewTag(normalizeTag(tag)); - setTagInput(""); - setShowTagSuggestions(false); - }; + setNewTag(normalizeTag(tag)) + setTagInput('') + setShowTagSuggestions(false) + } const handleRemoveTag = () => { - setNewTag(""); - setTagInput(""); - }; + setNewTag('') + setTagInput('') + } const getFilteredTags = () => { - const input = normalizeTag(tagInput.replace("/", "")); + const input = normalizeTag(tagInput.replace('/', '')) const allAvailableTags = [...PREDEFINED_TAGS, ...customTags].map( - normalizeTag, - ); + normalizeTag + ) return allAvailableTags.filter( - (tag) => tag.toLowerCase().includes(input) && tag !== newTag, - ); - }; + (tag) => tag.toLowerCase().includes(input) && tag !== newTag + ) + } const handleKeyDownTag = (e) => { - if (e.key === "Enter" && tagInput.startsWith("/")) { - e.preventDefault(); - const customTag = normalizeTag(tagInput.slice(1)); + if (e.key === 'Enter' && tagInput.startsWith('/')) { + e.preventDefault() + const customTag = normalizeTag(tagInput.slice(1)) if (customTag) { - handleSelectTag(customTag); + handleSelectTag(customTag) } } - }; + } const handleSaveChanges = async () => { try { const editEntry = { id: password.id, - type: "password", + type: 'password', name: newName, website: newWebsite, username: newUsername, password: newPassword, - tag: newTag ? normalizeTag(newTag) : "", + tag: newTag ? normalizeTag(newTag) : '', createdAt: password.createdAt, - editedAt: new Date().toISOString(), - }; - await vaultPass.edit(password.id, editEntry); - setIsEditMode(false); - setShowSuccessAlert(true); - useGlobalStore.getState().triggerRefreshPasswords(); + editedAt: new Date().toISOString() + } + await vaultPass.edit(password.id, editEntry) + setIsEditMode(false) + setShowSuccessAlert(true) + useGlobalStore.getState().triggerRefreshPasswords() setTimeout(() => { - setShowSuccessAlert(false); - onClose(); - }, 2000); + setShowSuccessAlert(false) + onClose() + }, 2000) } catch (error) { - console.error("Error updating password:", error); - alert("Failed to update password"); + console.error('Error updating password:', error) + alert('Failed to update password') } - }; + } const handleCancel = () => { - setNewName(password.name); - setNewWebsite(password.website); - setNewUsername(password.username); - setNewPassword(password.password); - setNewTag(password.tag ? normalizeTag(password.tag) : ""); - setTagInput(""); - setShowTagSuggestions(false); - setIsEditMode(false); - }; + setNewName(password.name) + setNewWebsite(password.website) + setNewUsername(password.username) + setNewPassword(password.password) + setNewTag(password.tag ? normalizeTag(password.tag) : '') + setTagInput('') + setShowTagSuggestions(false) + setIsEditMode(false) + } return ( <> {showSuccessAlert && ( } - color="var(--text-jetBrains)" - title="Success!" - message="Password updated successfully." + color='var(--text-jetBrains)' + title='Success!' + message='Password updated successfully.' onClose={() => setShowSuccessAlert(false)} /> )}
-
e.stopPropagation()}> -
+
e.stopPropagation()}> +

{newName}

-
-
-
+
+
setNewUsername(e.target.value)} - label="Username" - placeholder="Enter username" + label='Username' + placeholder='Enter username' />
-
+
setNewWebsite(e.target.value)} - label="Website" - placeholder="Enter website" + label='Website' + placeholder='Enter website' />
-
+
setNewPassword(e.target.value)} - label="Password" - placeholder="Enter password" + label='Password' + placeholder='Enter password' />
-
+
-
+
handleTagInput(e.target.value)} onKeyDown={handleKeyDownTag} - label="" - placeholder="Type /work or /personal (or custom)" + label='' + placeholder='Type /work or /personal (or custom)' /> - {showTagSuggestions && tagInput.startsWith("/") && ( -
+ {showTagSuggestions && tagInput.startsWith('/') && ( +
{getFilteredTags().length > 0 ? getFilteredTags().map((tag) => ( -
handleSelectTag(tag)} - className="dropdown-list" - > - /{tag} - {!PREDEFINED_TAGS.includes(tag) && ( -

- (custom) -

- )} -
- )) +
handleSelectTag(tag)} + className='dropdown-list' + > + /{tag} + {!PREDEFINED_TAGS.includes(tag) && ( +

+ (custom) +

+ )} +
+ )) : null} {normalizeTag(tagInput.slice(1)) && ![...PREDEFINED_TAGS, ...customTags] .map(normalizeTag) .includes(normalizeTag(tagInput.slice(1))) && ( -
- handleSelectTag(normalizeTag(tagInput.slice(1))) - } - className="dropdown-list" - style={{ fontStyle: "italic", opacity: 0.8 }} - > - Create tag: /{normalizeTag(tagInput.slice(1))} -
- )} +
+ handleSelectTag(normalizeTag(tagInput.slice(1)))} + className='dropdown-list' + style={{ fontStyle: 'italic', opacity: 0.8 }} + > + Create tag: /{normalizeTag(tagInput.slice(1))} +
+ )}
)} {newTag && (
-

+

Created {timeAgo(password.createdAt)}

-
- {!isEditMode ? ( - <> -
- ); -}; + ) +} diff --git a/src/components/menu/ShortMenu.jsx b/src/components/menu/ShortMenu.jsx index 4674b1a..5a3e71e 100644 --- a/src/components/menu/ShortMenu.jsx +++ b/src/components/menu/ShortMenu.jsx @@ -1,59 +1,59 @@ -import React from "react"; +import React from 'react' export const SortMenu = ({ sortOrder, setSortOrder, setShowSortMenu }) => { const options = [ - { value: "latest", label: "Newest First" }, - { value: "oldest", label: "Oldest First" }, - { value: "aToZ", label: "Name (A to Z)" }, - { value: "zToA", label: "Name (Z to A)" }, - ]; + { value: 'latest', label: 'Newest First' }, + { value: 'oldest', label: 'Oldest First' }, + { value: 'aToZ', label: 'Name (A to Z)' }, + { value: 'zToA', label: 'Name (Z to A)' } + ] return ( -
+
{options.map((option) => (
{ - setSortOrder(option.value); - setShowSortMenu(false); + setSortOrder(option.value) + setShowSortMenu(false) }} - className="dropdown-list" - style={{ fontSize: "14px" }} + className='dropdown-list' + style={{ fontSize: '14px' }} > {option.label}
))}
- ); -}; + ) +} export const getSortLabel = (order) => { switch (order) { - case "aToZ": - return "Name (A to Z)"; - case "zToA": - return "Name (Z to A)"; - case "latest": - return "Newest First"; - case "oldest": - return "Oldest First"; + case 'aToZ': + return 'Name (A to Z)' + case 'zToA': + return 'Name (Z to A)' + case 'latest': + return 'Newest First' + case 'oldest': + return 'Oldest First' default: - return "Sort by"; + return 'Sort by' } -}; +} export const sortPasswords = (passwords, order) => { return [...passwords].sort((a, b) => { switch (order) { - case "aToZ": - return (a.name || "").localeCompare(b.name || ""); - case "zToA": - return (b.name || "").localeCompare(a.name || ""); - case "oldest": - return a.createdAt - b.createdAt; - case "latest": + case 'aToZ': + return (a.name || '').localeCompare(b.name || '') + case 'zToA': + return (b.name || '').localeCompare(a.name || '') + case 'oldest': + return a.createdAt - b.createdAt + case 'latest': default: - return b.createdAt - a.createdAt; + return b.createdAt - a.createdAt } - }); -}; + }) +} diff --git a/src/components/notes/NoteCard.jsx b/src/components/notes/NoteCard.jsx index eb6bb76..4c363da 100644 --- a/src/components/notes/NoteCard.jsx +++ b/src/components/notes/NoteCard.jsx @@ -1,75 +1,75 @@ -import React from "react"; -import { Divider } from "@/src/components/ui/Divider.jsx"; -import { timeAgo } from "@/src/utils/timeAgo.js"; -import { FiFileText, FiTrash } from "react-icons/fi"; -import "@/src/App.css"; +import React from 'react' +import { Divider } from '@/src/components/ui/Divider.jsx' +import { timeAgo } from '@/src/utils/timeAgo.js' +import { FiFileText, FiTrash } from 'react-icons/fi' +import '@/src/App.css' export const NoteCard = ({ data, onDelete, onClick }) => { const title = - data?.content?.find((item) => item.type === "title")?.content || null; + data?.content?.find((item) => item.type === 'title')?.content || null const preview = data?.content - ?.filter((item) => item.type !== "title") + ?.filter((item) => item.type !== 'title') ?.map((item) => item.content) - ?.join(" ") - ?.substring(0, 100) + "..." || "No content"; + ?.join(' ') + ?.substring(0, 100) + '...' || 'No content' return ( -
-
+
+
- + - - {title &&

{title}

} + + {title &&

{title}

}

{preview}

- +
-
+

{timeAgo(data.createdAt)}

- ); -}; + ) +} diff --git a/src/components/notes/NoteSideBar.jsx b/src/components/notes/NoteSideBar.jsx index 9614547..6e6ee5d 100644 --- a/src/components/notes/NoteSideBar.jsx +++ b/src/components/notes/NoteSideBar.jsx @@ -1,121 +1,123 @@ -import React, { useState } from "react"; -import { FiX, FiTrash2, FiEdit, FiSave } from "react-icons/fi"; -import { timeAgo } from "@/src/utils/timeAgo"; -import { Button } from "@/src/components/ui/Button.jsx"; -import { Input } from "@/src/components/ui/Input.jsx"; -import "@/src/App.css"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import Alert from "@/src/components/popups/Alert.jsx"; +import React, { useState } from 'react' +import { FiX, FiTrash2, FiEdit, FiSave } from 'react-icons/fi' +import { timeAgo } from '@/src/utils/timeAgo' +import { Button } from '@/src/components/ui/Button.jsx' +import { Input } from '@/src/components/ui/Input.jsx' +import '@/src/App.css' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import Alert from '@/src/components/popups/Alert.jsx' export const NoteSideBar = ({ note, onClose, onDelete }) => { - if (!note) return null; + if (!note) return null - const [isEditMode, setIsEditMode] = useState(false); - const [showSuccessAlert, setShowSuccessAlert] = useState(false); - const [searchQuery, setSearchQuery] = useState(""); + const [isEditMode, setIsEditMode] = useState(false) + const [showSuccessAlert, setShowSuccessAlert] = useState(false) + const [searchQuery, setSearchQuery] = useState('') React.useEffect(() => { const fetchExistingTags = async () => { try { - await vaultPass.getAll(); + await vaultPass.getAll() } catch (error) { - console.error("Error fetching tags:", error); + console.error('Error fetching tags:', error) } - }; + } if (isEditMode) { - fetchExistingTags(); + fetchExistingTags() } - }, [isEditMode]); + }, [isEditMode]) const handleSaveEdit = async () => { try { const updatedNote = { - ...note, - }; - await vaultPass.update(note.id, updatedNote); - setIsEditMode(false); - setShowSuccessAlert(true); - setTimeout(() => setShowSuccessAlert(false), 2000); - useGlobalStore.getState().triggerRefreshNotes?.(); + ...note + } + await vaultPass.update(note.id, updatedNote) + setIsEditMode(false) + setShowSuccessAlert(true) + setTimeout(() => setShowSuccessAlert(false), 2000) + useGlobalStore.getState().triggerRefreshNotes?.() } catch (error) { - console.error("Error updating note:", error); + console.error('Error updating note:', error) } - }; + } - const normalizedQuery = searchQuery.trim().toLowerCase(); + const normalizedQuery = searchQuery.trim().toLowerCase() const filteredContent = normalizedQuery ? note?.content?.filter((item) => - item?.content?.toLowerCase().includes(normalizedQuery), - ) - : note?.content; + item?.content?.toLowerCase().includes(normalizedQuery) + ) + : note?.content - const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const highlightMatch = (text) => { - if (!normalizedQuery || !text) return text; - const regex = new RegExp(`(${escapeRegExp(normalizedQuery)})`, "ig"); - const parts = text.split(regex); + if (!normalizedQuery || !text) return text + const regex = new RegExp(`(${escapeRegExp(normalizedQuery)})`, 'ig') + const parts = text.split(regex) return parts.map((part, index) => - regex.test(part) ? ( - - {part} - - ) : ( - {part} - ), - ); - }; + regex.test(part) + ? ( + + {part} + + ) + : ( + {part} + ) + ) + } return ( <> {showSuccessAlert && ( - + )}
-
e.stopPropagation()}> +
e.stopPropagation()}> {/* Header */} -
+

{timeAgo(note.createdAt)}

-
{/* Note Content */} -
+
setSearchQuery(e.target.value)} />
{filteredContent?.map((item, idx) => (
- {item.type === "title" && ( -

+ {item.type === 'title' && ( +

{highlightMatch(item.content)}

)} - {item.type === "subtitle" && ( -

+ {item.type === 'subtitle' && ( +

{highlightMatch(item.content)}

)} - {item.type === "para" && ( -

+ {item.type === 'para' && ( +

{highlightMatch(item.content)}

)} @@ -124,44 +126,46 @@ export const NoteSideBar = ({ note, onClose, onDelete }) => {
{/* Action Buttons */} -
+
- ); -}; + ) +} diff --git a/src/components/password/PasswordCard.jsx b/src/components/password/PasswordCard.jsx index 3880ed1..9e17809 100644 --- a/src/components/password/PasswordCard.jsx +++ b/src/components/password/PasswordCard.jsx @@ -1,88 +1,88 @@ -import React from "react"; -import { Divider } from "@/src/components/ui/Divider.jsx"; -import PasswordInput from "@/src/components/password/PasswordInput.jsx"; -import { Tag } from "@/src/components/ui/Tag.jsx"; -import { timeAgo } from "@/src/utils/timeAgo.js"; -import { FiLock, FiTrash } from "react-icons/fi"; -import "@/src/App.css"; +import React from 'react' +import { Divider } from '@/src/components/ui/Divider.jsx' +import PasswordInput from '@/src/components/password/PasswordInput.jsx' +import { Tag } from '@/src/components/ui/Tag.jsx' +import { timeAgo } from '@/src/utils/timeAgo.js' +import { FiLock, FiTrash } from 'react-icons/fi' +import '@/src/App.css' export const PasswordCard = ({ data, onDelete, onClick }) => { - const title = data?.name || "Untitled"; - const website = data?.website || "No website"; - const username = data?.username || "No username"; - const password = data?.password || ""; - const tags = data?.tag || []; + const title = data?.name || 'Untitled' + const website = data?.website || 'No website' + const username = data?.username || 'No username' + const password = data?.password || '' + const tags = data?.tag || [] return ( -
-
+
+
- + - -

{title}

+ +

{title}

{website}

-

{username}

-
+

{username}

+
{}} />
- +
{tags.length > 0 && ( -
- +
+
)} -
+
{/* */}

{timeAgo(data.createdAt)}

- ); -}; + ) +} diff --git a/src/components/password/PasswordInput.jsx b/src/components/password/PasswordInput.jsx index dbf4204..803d6a8 100644 --- a/src/components/password/PasswordInput.jsx +++ b/src/components/password/PasswordInput.jsx @@ -1,82 +1,84 @@ -import React, { useState } from "react"; -import { FiCopy, FiEye, FiEyeOff } from "react-icons/fi"; +import React, { useState } from 'react' +import { FiCopy, FiEye, FiEyeOff } from 'react-icons/fi' const PasswordInput = ({ value, onChange, - placeholder = "Enter password", - label = null, + placeholder = 'Enter password', + label = null }) => { - const [showPassword, setShowPassword] = useState(false); - const [copied, setCopied] = useState(false); + const [showPassword, setShowPassword] = useState(false) + const [copied, setCopied] = useState(false) const togglePassword = () => { - setShowPassword((prev) => !prev); - }; + setShowPassword((prev) => !prev) + } const copyPassword = async () => { - if (!value) return; + if (!value) return try { - await navigator.clipboard.writeText(value); - setCopied(true); - setTimeout(() => setCopied(false), 1500); + await navigator.clipboard.writeText(value) + setCopied(true) + setTimeout(() => setCopied(false), 1500) } catch (err) { - console.error("Failed to copy:", err); + console.error('Failed to copy:', err) } - }; + } return ( <> {label !== null && ( )} -
+
- {copied && Copied!} + {copied && Copied!}
- ); -}; + ) +} -export default PasswordInput; +export default PasswordInput diff --git a/src/components/password/Search.jsx b/src/components/password/Search.jsx index 24201f2..3dd2fdd 100644 --- a/src/components/password/Search.jsx +++ b/src/components/password/Search.jsx @@ -1,44 +1,44 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef } from 'react' -export const Search = ({ content = "Search...", value, onChange }) => { - const inputRef = useRef(null); +export const Search = ({ content = 'Search...', value, onChange }) => { + const inputRef = useRef(null) useEffect(() => { const handleKeyDown = (e) => { - if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { - e.preventDefault(); - inputRef.current?.focus(); + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { + e.preventDefault() + inputRef.current?.focus() } - }; + } - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, []); + window.addEventListener('keydown', handleKeyDown) + return () => window.removeEventListener('keydown', handleKeyDown) + }, []) return ( -
+
- - + + - ⌘K + ⌘K
- ); -}; + ) +} diff --git a/src/components/popups/Alert.jsx b/src/components/popups/Alert.jsx index 24f3d5a..bbbef05 100644 --- a/src/components/popups/Alert.jsx +++ b/src/components/popups/Alert.jsx @@ -1,53 +1,53 @@ -import React, { useEffect, useState } from "react"; -import { FiX } from "react-icons/fi"; -import "@/src/App.css"; +import React, { useEffect, useState } from 'react' +import { FiX } from 'react-icons/fi' +import '@/src/App.css' -export default function Alert({ +export default function Alert ({ icon, - color = "#4f46e5", + color = '#4f46e5', title, message, duration = 4000, - onClose, + onClose }) { - const [visible, setVisible] = useState(false); + const [visible, setVisible] = useState(false) useEffect(() => { - setVisible(true); + setVisible(true) if (duration) { const timer = setTimeout(() => { - handleClose(); - }, duration); + handleClose() + }, duration) - return () => clearTimeout(timer); + return () => clearTimeout(timer) } - }, []); + }, []) const handleClose = () => { - setVisible(false); + setVisible(false) setTimeout(() => { - onClose && onClose(); - }, 300); - }; + onClose && onClose() + }, 300) + } return (
-
+
{icon}
-
-

{title}

-

{message}

+
+

{title}

+

{message}

-
- ); + ) } diff --git a/src/components/popups/DecryptPopUp.jsx b/src/components/popups/DecryptPopUp.jsx index 23629b2..85023e2 100644 --- a/src/components/popups/DecryptPopUp.jsx +++ b/src/components/popups/DecryptPopUp.jsx @@ -1,70 +1,70 @@ -import React from "react"; -import { FiX, FiUpload } from "react-icons/fi"; -import { Input } from "@/src/components/ui/Input.jsx"; -import { Button } from "@/src/components/ui/Button.jsx"; -import "@/src/App.css"; +import React from 'react' +import { FiX, FiUpload } from 'react-icons/fi' +import { Input } from '@/src/components/ui/Input.jsx' +import { Button } from '@/src/components/ui/Button.jsx' +import '@/src/App.css' export const DecryptPopUp = ({ roomKey, setRoomKey, decryptError, handleDecryptAndImport, - handleCancelDecrypt, + handleCancelDecrypt }) => { return ( -
-
+
+

Decrypt Vault Data

-
-
-

+

+

Enter the room key (invite code) to decrypt and import the vault data.

setRoomKey(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && handleDecryptAndImport()} + onKeyDown={(e) => e.key === 'Enter' && handleDecryptAndImport()} /> {decryptError && (

{decryptError}

)}
-
+
- ); -}; + ) +} diff --git a/src/components/ui/Button.jsx b/src/components/ui/Button.jsx index 45b8289..c434928 100644 --- a/src/components/ui/Button.jsx +++ b/src/components/ui/Button.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React from 'react' export const Button = ({ content, @@ -6,26 +6,26 @@ export const Button = ({ iconSize = 14, bgColor, onClick, - type = "button", + type = 'button', disabled = false, - className = "button", - style = {}, + className = 'button', + style = {} }) => { return ( - ); -}; + ) +} diff --git a/src/components/ui/Divider.jsx b/src/components/ui/Divider.jsx index 916fd2c..03d18e5 100644 --- a/src/components/ui/Divider.jsx +++ b/src/components/ui/Divider.jsx @@ -1,8 +1,8 @@ -import React from "react"; -import "@/src/App.css"; +import React from 'react' +import '@/src/App.css' -export const Divider = ({ margin, bgColor = "var(--border)" }) => { +export const Divider = ({ margin, bgColor = 'var(--border)' }) => { return ( -
- ); -}; +
+ ) +} diff --git a/src/components/ui/Input.jsx b/src/components/ui/Input.jsx index 47bacb5..4c5b0f1 100644 --- a/src/components/ui/Input.jsx +++ b/src/components/ui/Input.jsx @@ -1,16 +1,16 @@ -import React from "react"; +import React from 'react' export const Input = ({ label, error, ...props }) => { return ( -
+
- - {error && {error}} + + {error && {error}}
- ); -}; + ) +} diff --git a/src/components/ui/Loader.jsx b/src/components/ui/Loader.jsx index 57965b0..a90eda1 100644 --- a/src/components/ui/Loader.jsx +++ b/src/components/ui/Loader.jsx @@ -1,19 +1,19 @@ -import React from "react"; -import "../../App.css"; +import React from 'react' +import '../../App.css' -export const Loader = ({ size = 40, color = "var(--bg)", thickness = 4 }) => { +export const Loader = ({ size = 40, color = 'var(--bg)', thickness = 4 }) => { return ( -
+
- ); -}; + ) +} diff --git a/src/components/ui/Tag.jsx b/src/components/ui/Tag.jsx index 944e649..2a38f80 100644 --- a/src/components/ui/Tag.jsx +++ b/src/components/ui/Tag.jsx @@ -1,12 +1,12 @@ -import React from "react"; +import React from 'react' export const Tag = ({ content, color }) => { return (
{content}
- ); -}; + ) +} diff --git a/src/components/ui/ToggleSwitch.jsx b/src/components/ui/ToggleSwitch.jsx index 67dbb98..82c5c1c 100644 --- a/src/components/ui/ToggleSwitch.jsx +++ b/src/components/ui/ToggleSwitch.jsx @@ -1,34 +1,34 @@ -import React from "react"; -import { useOptionStore } from "@/src/store/optionStore.js"; -import { FiLock, FiFileText } from "react-icons/fi"; +import React from 'react' +import { useOptionStore } from '@/src/store/optionStore.js' +import { FiLock, FiFileText } from 'react-icons/fi' const ToggleSwitch = () => { - const mode = useOptionStore((state) => state.mode); - const setMode = useOptionStore((state) => state.setMode); + const mode = useOptionStore((state) => state.mode) + const setMode = useOptionStore((state) => state.setMode) return ( -
-
+
+
- ); -}; + ) +} -export default ToggleSwitch; +export default ToggleSwitch diff --git a/src/features/notes/AddNoteForm.jsx b/src/features/notes/AddNoteForm.jsx index 46953ce..1779438 100644 --- a/src/features/notes/AddNoteForm.jsx +++ b/src/features/notes/AddNoteForm.jsx @@ -1,134 +1,134 @@ /* global alert */ -import React, { useState, useRef } from "react"; -import { Button } from "@/src/components/ui/Button"; -import { FiPlus, FiX } from "react-icons/fi"; -import "@/src/App.css"; -import { useGlobalStore } from "../../store/globalStore"; -import { vaultPass } from "../../lib/vaultPass"; +import React, { useState, useRef } from 'react' +import { Button } from '@/src/components/ui/Button' +import { FiPlus, FiX } from 'react-icons/fi' +import '@/src/App.css' +import { useGlobalStore } from '../../store/globalStore' +import { vaultPass } from '../../lib/vaultPass' export const AddNoteForm = () => { const [blocks, setBlocks] = useState([ - { id: Date.now(), type: "para", content: "" }, - ]); + { id: Date.now(), type: 'para', content: '' } + ]) - const [showDropdown, setShowDropdown] = useState(false); - const [activeBlockId, setActiveBlockId] = useState(null); + const [showDropdown, setShowDropdown] = useState(false) + const [activeBlockId, setActiveBlockId] = useState(null) - const blockRefs = useRef({}); + const blockRefs = useRef({}) const COMMANDS = { - "/title": "title", - "/subtitle": "subtitle", - "/para": "para", - }; + '/title': 'title', + '/subtitle': 'subtitle', + '/para': 'para' + } const handleBlockChange = (id, value) => { - const trimmed = value.trim(); + const trimmed = value.trim() const command = Object.keys(COMMANDS).find((cmd) => - trimmed.startsWith(cmd), - ); + trimmed.startsWith(cmd) + ) setBlocks((prev) => prev.map((block) => { - if (block.id !== id) return block; + if (block.id !== id) return block if (command) { - setShowDropdown(false); + setShowDropdown(false) return { ...block, type: COMMANDS[command], - content: value.replace(command, "").trim(), - }; + content: value.replace(command, '').trim() + } } - if (value === "/") { - setShowDropdown(true); - setActiveBlockId(id); + if (value === '/') { + setShowDropdown(true) + setActiveBlockId(id) } else { - setShowDropdown(false); + setShowDropdown(false) } - return { ...block, content: value }; - }), - ); - }; + return { ...block, content: value } + }) + ) + } const handleKeyDown = (e, id) => { - if (e.key === "Enter") { - const currentBlock = blocks.find((b) => b.id === id); + if (e.key === 'Enter') { + const currentBlock = blocks.find((b) => b.id === id) - e.preventDefault(); + e.preventDefault() const newBlock = { id: Date.now(), type: currentBlock.type, - content: "", - }; + content: '' + } - const index = blocks.findIndex((b) => b.id === id); - const updated = [...blocks]; - updated.splice(index + 1, 0, newBlock); + const index = blocks.findIndex((b) => b.id === id) + const updated = [...blocks] + updated.splice(index + 1, 0, newBlock) - setBlocks(updated); + setBlocks(updated) setTimeout(() => { - blockRefs.current[newBlock.id]?.focus(); - }, 0); + blockRefs.current[newBlock.id]?.focus() + }, 0) } - }; + } const handleCommandSelect = (type) => { setBlocks((prev) => prev.map((block) => - block.id === activeBlockId ? { ...block, type, content: "" } : block, - ), - ); + block.id === activeBlockId ? { ...block, type, content: '' } : block + ) + ) - setShowDropdown(false); + setShowDropdown(false) setTimeout(() => { - blockRefs.current[activeBlockId]?.focus(); - }, 0); - }; + blockRefs.current[activeBlockId]?.focus() + }, 0) + } const handleAddNote = async () => { - const filtered = blocks.filter((b) => b.content.trim() !== ""); + const filtered = blocks.filter((b) => b.content.trim() !== '') if (filtered.length === 0) { - alert("Please add some content"); - return; + alert('Please add some content') + return } const noteData = { - type: "note", + type: 'note', content: filtered, - createdAt: new Date().toISOString(), - }; + createdAt: new Date().toISOString() + } try { - await vaultPass.add(noteData); - useGlobalStore.getState().triggerRefreshNotes(); - useGlobalStore.getState().toggleNoteForm(); - setBlocks([{ id: Date.now(), type: "para", content: "" }]); + await vaultPass.add(noteData) + useGlobalStore.getState().triggerRefreshNotes() + useGlobalStore.getState().toggleNoteForm() + setBlocks([{ id: Date.now(), type: 'para', content: '' }]) } catch (e) { - console.error("Error saving note:", e); - alert("Failed to save note"); + console.error('Error saving note:', e) + alert('Failed to save note') } - }; + } const handleCancel = () => { - setBlocks([{ id: Date.now(), type: "para", content: "" }]); - useGlobalStore.getState().toggleNoteForm(); - }; + setBlocks([{ id: Date.now(), type: 'para', content: '' }]) + useGlobalStore.getState().toggleNoteForm() + } return ( -
e.preventDefault()}> + e.preventDefault()}> {/* Header */}

New Note

@@ -137,73 +137,73 @@ export const AddNoteForm = () => { {/* Parent Container */}
{blocks.map((block) => { - const isParagraph = block.type === "para"; + const isParagraph = block.type === 'para' const commonProps = { key: block.id, value: block.content, - "data-type": block.type, - className: "note-block-input", + 'data-type': block.type, + className: 'note-block-input', ref: (el) => (blockRefs.current[block.id] = el), onChange: (e) => handleBlockChange(block.id, e.target.value), onKeyDown: (e) => handleKeyDown(e, block.id), style: { - width: "100%", - border: "none", - outline: "none", - background: "transparent", - marginBottom: "0.5rem", - resize: "none", - }, - }; + width: '100%', + border: 'none', + outline: 'none', + background: 'transparent', + marginBottom: '0.5rem', + resize: 'none' + } + } return ( - ); + ) })} {/* Dropdown */} {showDropdown && (
handleCommandSelect("title")} + className='dropdown-list' + onClick={() => handleCommandSelect('title')} > /title
handleCommandSelect("subtitle")} + className='dropdown-list' + onClick={() => handleCommandSelect('subtitle')} > /subtitle
handleCommandSelect("para")} + className='dropdown-list' + onClick={() => handleCommandSelect('para')} > /para
@@ -214,36 +214,36 @@ export const AddNoteForm = () => { {/* Footer Buttons */}
- ); -}; + ) +} diff --git a/src/features/notes/NoteManager.jsx b/src/features/notes/NoteManager.jsx index 7d5427a..503a0e9 100644 --- a/src/features/notes/NoteManager.jsx +++ b/src/features/notes/NoteManager.jsx @@ -1,134 +1,134 @@ -import React, { useEffect, useState } from "react"; -import { NoteCard } from "@/src/components/notes/NoteCard.jsx"; -import { NoteSideBar } from "@/src/components/notes/NoteSideBar.jsx"; -import { Search } from "@/src/components/password/Search.jsx"; -import { Button } from "@/src/components/ui/Button.jsx"; -import { Divider } from "@/src/components/ui/Divider.jsx"; +import React, { useEffect, useState } from 'react' +import { NoteCard } from '@/src/components/notes/NoteCard.jsx' +import { NoteSideBar } from '@/src/components/notes/NoteSideBar.jsx' +import { Search } from '@/src/components/password/Search.jsx' +import { Button } from '@/src/components/ui/Button.jsx' +import { Divider } from '@/src/components/ui/Divider.jsx' import { getSortLabel, SortMenu, - sortPasswords, -} from "@/src/components/menu/ShortMenu.jsx"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import VaultBG from "../../assets/Vault_BG.png"; -import { FiChevronDown, FiFilter } from "react-icons/fi"; + sortPasswords +} from '@/src/components/menu/ShortMenu.jsx' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import VaultBG from '../../assets/Vault_BG.png' +import { FiChevronDown, FiFilter } from 'react-icons/fi' export const NoteManager = () => { - const [notes, setNotes] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [searchQuery, setSearchQuery] = useState(""); - const [selectedNote, setSelectedNote] = useState(null); + const [notes, setNotes] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [searchQuery, setSearchQuery] = useState('') + const [selectedNote, setSelectedNote] = useState(null) const refreshNotesTrigger = useGlobalStore( - (state) => state.refreshNotesTrigger, - ); - const [sortOrder, setSortOrder] = useState("latest"); - const [showSortMenu, setShowSortMenu] = useState(false); + (state) => state.refreshNotesTrigger + ) + const [sortOrder, setSortOrder] = useState('latest') + const [showSortMenu, setShowSortMenu] = useState(false) useEffect(() => { - let isMounted = true; + let isMounted = true const fetchNotes = async () => { try { - setIsLoading(true); - const entries = await vaultPass.getAll(); - const notesOnly = entries.filter((entry) => entry.type === "note"); + setIsLoading(true) + const entries = await vaultPass.getAll() + const notesOnly = entries.filter((entry) => entry.type === 'note') if (isMounted) { - setNotes(notesOnly); + setNotes(notesOnly) } } catch (error) { - console.error("Error fetching notes:", error); + console.error('Error fetching notes:', error) } finally { if (isMounted) { - setIsLoading(false); + setIsLoading(false) } } - }; + } const refresh = async () => { if (isMounted) { - const updated = await vaultPass.getAll(); - setNotes(updated); + const updated = await vaultPass.getAll() + setNotes(updated) } - }; + } - fetchNotes(); - vaultPass.autoSync(refresh); + fetchNotes() + vaultPass.autoSync(refresh) return () => { - isMounted = false; - }; - }, [refreshNotesTrigger, setNotes]); + isMounted = false + } + }, [refreshNotesTrigger, setNotes]) if (isLoading) { - return
Loading...
; + return
Loading...
} const handleDelete = async (id) => { try { - await vaultPass.delete(id); - setNotes((prev) => prev.filter((note) => note.id !== id)); - setSelectedNote(null); + await vaultPass.delete(id) + setNotes((prev) => prev.filter((note) => note.id !== id)) + setSelectedNote(null) } catch (e) { - console.error("Error deleting note:", e); + console.error('Error deleting note:', e) } - }; + } const filteredAndSortedNotes = sortPasswords( notes.filter((note) => { - if (note.type !== "note") return false; - const normalizedQuery = searchQuery.toLowerCase(); + if (note.type !== 'note') return false + const normalizedQuery = searchQuery.toLowerCase() const matchesTitle = note?.content - ?.find((item) => item.type === "title") + ?.find((item) => item.type === 'title') ?.content?.toLowerCase() - .includes(normalizedQuery); + .includes(normalizedQuery) const matchesContent = note?.content?.some((item) => - item?.content?.toLowerCase().includes(normalizedQuery), - ); - const matchesSearch = !searchQuery || matchesTitle || matchesContent; + item?.content?.toLowerCase().includes(normalizedQuery) + ) + const matchesSearch = !searchQuery || matchesTitle || matchesContent - return matchesSearch; + return matchesSearch }), - sortOrder, - ); + sortOrder + ) const handleSearch = (query) => { - setSearchQuery(query); - }; + setSearchQuery(query) + } return ( <> -
+

{filteredAndSortedNotes.length} note - {filteredAndSortedNotes.length !== 1 ? "s" : ""} + {filteredAndSortedNotes.length !== 1 ? 's' : ''}

handleSearch(e.target.value)} /> -
+
- - {filteredAndSortedNotes.length === 0 ? ( -
- Empty vault -

No notes found

-
- ) : ( -
- {filteredAndSortedNotes.map((note) => ( - setSelectedNote(note)} - onDelete={() => handleDelete(note.id)} + + {filteredAndSortedNotes.length === 0 + ? ( +
+ Empty vault - ))} -
- )} +

No notes found

+
+ ) + : ( +
+ {filteredAndSortedNotes.map((note) => ( + setSelectedNote(note)} + onDelete={() => handleDelete(note.id)} + /> + ))} +
+ )}
{selectedNote && ( setSelectedNote(null)} onDelete={() => { - handleDelete(selectedNote.id); + handleDelete(selectedNote.id) }} /> )} - ); -}; + ) +} diff --git a/src/features/passwords/AddPasswordForm.jsx b/src/features/passwords/AddPasswordForm.jsx index 0183fc0..20887e2 100644 --- a/src/features/passwords/AddPasswordForm.jsx +++ b/src/features/passwords/AddPasswordForm.jsx @@ -1,92 +1,92 @@ /* global alert */ -import React from "react"; -import { Input } from "@/src/components/ui/Input"; -import { Button } from "@/src/components/ui/Button"; -import { Tag } from "@/src/components/ui/Tag"; -import PasswordInput from "@/src/components/password/PasswordInput"; -import { generatePassword } from "@/src/lib/generatePassword"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore"; -import { IoFlash } from "react-icons/io5"; -import { FiPlus, FiX } from "react-icons/fi"; -import "@/src/App.css"; +import React from 'react' +import { Input } from '@/src/components/ui/Input' +import { Button } from '@/src/components/ui/Button' +import { Tag } from '@/src/components/ui/Tag' +import PasswordInput from '@/src/components/password/PasswordInput' +import { generatePassword } from '@/src/lib/generatePassword' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore' +import { IoFlash } from 'react-icons/io5' +import { FiPlus, FiX } from 'react-icons/fi' +import '@/src/App.css' export const AddPasswordForm = () => { - const PREDEFINED_TAGS = ["work", "personal"]; + const PREDEFINED_TAGS = ['work', 'personal'] - const [name, setName] = React.useState(""); - const [url, setUrl] = React.useState(""); - const [username, setUsername] = React.useState(""); - const [tagInput, setTagInput] = React.useState(""); - const [selectedTag, setSelectedTag] = React.useState(""); - const [showTagSuggestions, setShowTagSuggestions] = React.useState(false); - const [customTags, setCustomTags] = React.useState([]); + const [name, setName] = React.useState('') + const [url, setUrl] = React.useState('') + const [username, setUsername] = React.useState('') + const [tagInput, setTagInput] = React.useState('') + const [selectedTag, setSelectedTag] = React.useState('') + const [showTagSuggestions, setShowTagSuggestions] = React.useState(false) + const [customTags, setCustomTags] = React.useState([]) - const normalizeTag = (tag) => tag.trim().toLowerCase(); + const normalizeTag = (tag) => tag.trim().toLowerCase() // Fetch all existing tags when component mounts React.useEffect(() => { const fetchExistingTags = async () => { try { - const entries = await vaultPass.getAll(); + const entries = await vaultPass.getAll() const allTags = entries .map((entry) => entry.tag) .filter((tag) => tag) .map(normalizeTag) .filter((tag) => tag && !PREDEFINED_TAGS.includes(tag)) - .filter((tag, index, self) => self.indexOf(tag) === index); - setCustomTags(allTags); + .filter((tag, index, self) => self.indexOf(tag) === index) + setCustomTags(allTags) } catch (error) { - console.error("Error fetching tags:", error); + console.error('Error fetching tags:', error) } - }; - fetchExistingTags(); - }, []); + } + fetchExistingTags() + }, []) - const generatedPassword = useGlobalStore((state) => state.generatedPassword); + const generatedPassword = useGlobalStore((state) => state.generatedPassword) const setGeneratedPassword = useGlobalStore( - (state) => state.setGeneratedPassword, - ); + (state) => state.setGeneratedPassword + ) const handleTagInput = (value) => { - setTagInput(value); - if (value.startsWith("/")) { - setShowTagSuggestions(true); + setTagInput(value) + if (value.startsWith('/')) { + setShowTagSuggestions(true) } else { - setShowTagSuggestions(false); + setShowTagSuggestions(false) } - }; + } const handleSelectTag = (tag) => { - setSelectedTag(normalizeTag(tag)); - setTagInput(""); - setShowTagSuggestions(false); - }; + setSelectedTag(normalizeTag(tag)) + setTagInput('') + setShowTagSuggestions(false) + } const handleKeyDown = (e) => { - if (e.key === "Enter" && tagInput.startsWith("/")) { - e.preventDefault(); - const customTag = normalizeTag(tagInput.slice(1)); + if (e.key === 'Enter' && tagInput.startsWith('/')) { + e.preventDefault() + const customTag = normalizeTag(tagInput.slice(1)) if (customTag) { - handleSelectTag(customTag); + handleSelectTag(customTag) } } - }; + } const handleRemoveTag = () => { - setSelectedTag(""); - setTagInput(""); - }; + setSelectedTag('') + setTagInput('') + } const getFilteredTags = () => { - const input = normalizeTag(tagInput.replace("/", "")); + const input = normalizeTag(tagInput.replace('/', '')) const allAvailableTags = [...PREDEFINED_TAGS, ...customTags].map( - normalizeTag, - ); + normalizeTag + ) return allAvailableTags.filter( - (tag) => tag.toLowerCase().includes(input) && tag !== selectedTag, - ); - }; + (tag) => tag.toLowerCase().includes(input) && tag !== selectedTag + ) + } const handleGeneratePassword = () => { const password = generatePassword({ @@ -94,181 +94,180 @@ export const AddPasswordForm = () => { useLower: true, useUpper: true, useNumbers: true, - useSymbols: false, - }); - setGeneratedPassword(password); - }; + useSymbols: false + }) + setGeneratedPassword(password) + } const calculatePasswordStrength = (password) => { if (!password) { - return { strength: 0, label: "None", color: "var(--text-secondary)" }; + return { strength: 0, label: 'None', color: 'var(--text-secondary)' } } - let strength = 0; + let strength = 0 // Length check - if (password.length >= 8) strength += 1; - if (password.length >= 12) strength += 1; - if (password.length >= 16) strength += 1; + if (password.length >= 8) strength += 1 + if (password.length >= 12) strength += 1 + if (password.length >= 16) strength += 1 // Character variety checks - if (/[a-z]/.test(password)) strength += 1; - if (/[A-Z]/.test(password)) strength += 1; - if (/[0-9]/.test(password)) strength += 1; - if (/[^a-zA-Z0-9]/.test(password)) strength += 1; + if (/[a-z]/.test(password)) strength += 1 + if (/[A-Z]/.test(password)) strength += 1 + if (/[0-9]/.test(password)) strength += 1 + if (/[^a-zA-Z0-9]/.test(password)) strength += 1 // Return strength level - if (strength <= 2) return { strength: 25, label: "Weak", color: "#ff4d4f" }; - if (strength <= 4) return { strength: 50, label: "Fair", color: "#faad14" }; - if (strength <= 5) return { strength: 75, label: "Good", color: "#52c41a" }; - return { strength: 100, label: "Strong", color: "var(--text-jetBrains)" }; - }; + if (strength <= 2) return { strength: 25, label: 'Weak', color: '#ff4d4f' } + if (strength <= 4) return { strength: 50, label: 'Fair', color: '#faad14' } + if (strength <= 5) return { strength: 75, label: 'Good', color: '#52c41a' } + return { strength: 100, label: 'Strong', color: 'var(--text-jetBrains)' } + } const handleAddPassword = async () => { if (!name || !url || !username || !generatedPassword) { - alert("Please fill in all fields"); - return; + alert('Please fill in all fields') + return } const passwordDetail = { - type: "password", + type: 'password', name, website: url, username, password: generatedPassword, - tag: selectedTag ? normalizeTag(selectedTag) : "", - createdAt: new Date().toISOString(), - }; + tag: selectedTag ? normalizeTag(selectedTag) : '', + createdAt: new Date().toISOString() + } try { - const newEntry = await vaultPass.add(passwordDetail); - console.log(newEntry); - setName(""); - setUrl(""); - setUsername(""); - setGeneratedPassword(""); - setSelectedTag(""); - setTagInput(""); + const newEntry = await vaultPass.add(passwordDetail) + console.log(newEntry) + setName('') + setUrl('') + setUsername('') + setGeneratedPassword('') + setSelectedTag('') + setTagInput('') - useGlobalStore.getState().triggerRefreshPasswords(); - useGlobalStore.getState().togglePasswordForm(); + useGlobalStore.getState().triggerRefreshPasswords() + useGlobalStore.getState().togglePasswordForm() } catch (error) { - console.error("Error saving password:", error); - alert("Failed to save password details"); + console.error('Error saving password:', error) + alert('Failed to save password details') } - }; + } - const passwordStrength = calculatePasswordStrength(generatedPassword); + const passwordStrength = calculatePasswordStrength(generatedPassword) return ( -
e.preventDefault()}> + e.preventDefault()}>

New Password Entry

End-to-end encrypted and stored locally

-
+
setName(e.target.value)} /> setUrl(e.target.value)} /> setUsername(e.target.value)} />
-
+
handleTagInput(e.target.value)} onKeyDown={handleKeyDown} /> - {showTagSuggestions && tagInput.startsWith("/") && ( -
+ {showTagSuggestions && tagInput.startsWith('/') && ( +
{getFilteredTags().length > 0 ? getFilteredTags().map((tag) => ( -
handleSelectTag(tag)} - className="dropdown-list" - > - /{tag} - {!PREDEFINED_TAGS.includes(tag) && ( -

- (custom) -

- )} -
- )) +
handleSelectTag(tag)} + className='dropdown-list' + > + /{tag} + {!PREDEFINED_TAGS.includes(tag) && ( +

+ (custom) +

+ )} +
+ )) : null} {normalizeTag(tagInput.slice(1)) && ![...PREDEFINED_TAGS, ...customTags] .map(normalizeTag) .includes(normalizeTag(tagInput.slice(1))) && ( -
- handleSelectTag(normalizeTag(tagInput.slice(1))) - } - className="dropdown-list" - style={{ fontStyle: "italic", opacity: 0.8 }} - > - Create tag: /{normalizeTag(tagInput.slice(1))} -
- )} +
+ handleSelectTag(normalizeTag(tagInput.slice(1)))} + className='dropdown-list' + style={{ fontStyle: 'italic', opacity: 0.8 }} + > + Create tag: /{normalizeTag(tagInput.slice(1))} +
+ )}
)}
{selectedTag && (
setGeneratedPassword(e.target.value)} />

Strength

@@ -314,60 +313,60 @@ export const AddPasswordForm = () => {
- ); -}; + ) +} diff --git a/src/features/passwords/GeneratePasswordForm.jsx b/src/features/passwords/GeneratePasswordForm.jsx index 50be8a5..1b5de02 100644 --- a/src/features/passwords/GeneratePasswordForm.jsx +++ b/src/features/passwords/GeneratePasswordForm.jsx @@ -1,30 +1,30 @@ -import React, { useState, useEffect } from "react"; -import PasswordInput from "@/src/components/password/PasswordInput.jsx"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import { Button } from "@/src/components/ui/Button.jsx"; -import { generatePassword } from "@/src/lib/generatePassword.js"; -import "@/src/App.css"; -import Alert from "@/src/components/popups/Alert.jsx"; -import { FaCopy } from "react-icons/fa"; +import React, { useState, useEffect } from 'react' +import PasswordInput from '@/src/components/password/PasswordInput.jsx' +import { useGlobalStore } from '@/src/store/globalStore.js' +import { Button } from '@/src/components/ui/Button.jsx' +import { generatePassword } from '@/src/lib/generatePassword.js' +import '@/src/App.css' +import Alert from '@/src/components/popups/Alert.jsx' +import { FaCopy } from 'react-icons/fa' export const GeneratePasswordForm = () => { const toggleGenerateForm = useGlobalStore( - (state) => state.toggleGenerateForm, - ); + (state) => state.toggleGenerateForm + ) const togglePasswordForm = useGlobalStore( - (state) => state.togglePasswordForm, - ); - const generatedPassword = useGlobalStore((state) => state.generatedPassword); - const [showSuccessAlert, setShowSuccessAlert] = React.useState(false); + (state) => state.togglePasswordForm + ) + const generatedPassword = useGlobalStore((state) => state.generatedPassword) + const [showSuccessAlert, setShowSuccessAlert] = React.useState(false) const setGeneratedPassword = useGlobalStore( - (state) => state.setGeneratedPassword, - ); + (state) => state.setGeneratedPassword + ) - const [length, setLength] = useState(16); - const [useLower, setUseLower] = useState(true); - const [useUpper, setUseUpper] = useState(true); - const [useNumbers, setUseNumbers] = useState(true); - const [useSymbols, setUseSymbols] = useState(false); + const [length, setLength] = useState(16) + const [useLower, setUseLower] = useState(true) + const [useUpper, setUseUpper] = useState(true) + const [useNumbers, setUseNumbers] = useState(true) + const [useSymbols, setUseSymbols] = useState(false) const handleGeneratePassword = () => { const password = generatePassword({ @@ -32,39 +32,39 @@ export const GeneratePasswordForm = () => { useLower, useUpper, useNumbers, - useSymbols, - }); - setGeneratedPassword(password); - }; + useSymbols + }) + setGeneratedPassword(password) + } const handleUsePassword = async () => { try { // Copy to clipboard - await navigator.clipboard.writeText(generatedPassword); - setShowSuccessAlert(true); - setGeneratedPassword(generatedPassword); - toggleGenerateForm(); - togglePasswordForm(); + await navigator.clipboard.writeText(generatedPassword) + setShowSuccessAlert(true) + setGeneratedPassword(generatedPassword) + toggleGenerateForm() + togglePasswordForm() } catch (error) { - console.error("Failed to copy password:", error); + console.error('Failed to copy password:', error) } - }; + } useEffect(() => { - handleGeneratePassword(); - }, []); + handleGeneratePassword() + }, []) return ( -
+
{showSuccessAlert && ( } - title="Success!" - message="Password copied to clipboard!" + title='Success!' + message='Password copied to clipboard!' onClose={() => setShowSuccessAlert(false)} /> )} -
+

Generate Password

Generate a cryptographically secure password

@@ -73,92 +73,92 @@ export const GeneratePasswordForm = () => { setGeneratedPassword(e.target.value)} - placeholder="Randomly Generated Password" + placeholder='Randomly Generated Password' />
setLength(Number(e.target.value))} />
-
+
- ); -}; + ) +} diff --git a/src/features/passwords/PasswordManager.jsx b/src/features/passwords/PasswordManager.jsx index 4656f37..e628408 100644 --- a/src/features/passwords/PasswordManager.jsx +++ b/src/features/passwords/PasswordManager.jsx @@ -1,90 +1,90 @@ -import React, { useEffect, useState } from "react"; -import { PasswordCard } from "@/src/components/password/PasswordCard.jsx"; -import { Search } from "@/src/components/password/Search.jsx"; -import { Button } from "@/src/components/ui/Button.jsx"; +import React, { useEffect, useState } from 'react' +import { PasswordCard } from '@/src/components/password/PasswordCard.jsx' +import { Search } from '@/src/components/password/Search.jsx' +import { Button } from '@/src/components/ui/Button.jsx' import { getSortLabel, SortMenu, - sortPasswords, -} from "@/src/components/menu/ShortMenu.jsx"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import VaultBG from "../../assets/Vault_BG.png"; -import { FiChevronDown, FiFilter } from "react-icons/fi"; + sortPasswords +} from '@/src/components/menu/ShortMenu.jsx' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import VaultBG from '../../assets/Vault_BG.png' +import { FiChevronDown, FiFilter } from 'react-icons/fi' export const PasswordManager = ({ passwords, setPasswords, onSelect }) => { - const [isLoading, setIsLoading] = useState(true); - const [selectedFilter, setSelectedFilter] = useState(null); - const [searchQuery, setSearchQuery] = useState(""); + const [isLoading, setIsLoading] = useState(true) + const [selectedFilter, setSelectedFilter] = useState(null) + const [searchQuery, setSearchQuery] = useState('') const refreshPasswordsTrigger = useGlobalStore( - (state) => state.refreshPasswordsTrigger, - ); - const [sortOrder, setSortOrder] = useState("latest"); - const [showSortMenu, setShowSortMenu] = useState(false); + (state) => state.refreshPasswordsTrigger + ) + const [sortOrder, setSortOrder] = useState('latest') + const [showSortMenu, setShowSortMenu] = useState(false) - const normalizeTag = (tag) => (tag || "").trim().toLowerCase(); + const normalizeTag = (tag) => (tag || '').trim().toLowerCase() useEffect(() => { - let isMounted = true; + let isMounted = true const fetchPasswords = async () => { try { - setIsLoading(true); - const entries = await vaultPass.getAll(); + setIsLoading(true) + const entries = await vaultPass.getAll() if (isMounted) { - setPasswords(entries); + setPasswords(entries) } } catch (error) { - console.error("Error fetching passwords:", error); + console.error('Error fetching passwords:', error) } finally { if (isMounted) { - setIsLoading(false); + setIsLoading(false) } } - }; + } const refresh = async () => { if (isMounted) { - const updated = await vaultPass.getAll(); - setPasswords(updated); + const updated = await vaultPass.getAll() + setPasswords(updated) } - }; + } - fetchPasswords(); - vaultPass.autoSync(refresh); + fetchPasswords() + vaultPass.autoSync(refresh) return () => { - isMounted = false; - }; - }, [refreshPasswordsTrigger, setPasswords]); + isMounted = false + } + }, [refreshPasswordsTrigger, setPasswords]) if (isLoading) { - return
Loading...
; + return
Loading...
} const handleDelete = async (id) => { try { - await vaultPass.delete(id); - setPasswords((prev) => prev.filter((pass) => pass.id !== id)); + await vaultPass.delete(id) + setPasswords((prev) => prev.filter((pass) => pass.id !== id)) } catch (e) { - console.error("Error deleting password:", e); + console.error('Error deleting password:', e) } - }; + } const getUniqueTags = () => { const tags = passwords - .filter((pass) => pass.type === "password") + .filter((pass) => pass.type === 'password') .map((pass) => normalizeTag(pass.tag)) .filter((tag) => tag) - .filter((tag, index, self) => self.indexOf(tag) === index); - return tags; - }; + .filter((tag, index, self) => self.indexOf(tag) === index) + return tags + } const filteredAndSortedPasswords = sortPasswords( passwords.filter((pass) => { - if (pass.type !== "password") return false; + if (pass.type !== 'password') return false const matchesTag = - !selectedFilter || normalizeTag(pass.tag) === selectedFilter; + !selectedFilter || normalizeTag(pass.tag) === selectedFilter const matchesSearch = !searchQuery || (pass.name && @@ -94,46 +94,46 @@ export const PasswordManager = ({ passwords, setPasswords, onSelect }) => { (pass.note && pass.note.toLowerCase().includes(searchQuery.toLowerCase())) || (pass.website && - pass.website.toLowerCase().includes(searchQuery.toLowerCase())); + pass.website.toLowerCase().includes(searchQuery.toLowerCase())) - return matchesTag && matchesSearch; + return matchesTag && matchesSearch }), - sortOrder, - ); + sortOrder + ) const handleTagClick = (tag) => { - const normalized = normalizeTag(tag); - setSelectedFilter(selectedFilter === normalized ? null : normalized); - }; + const normalized = normalizeTag(tag) + setSelectedFilter(selectedFilter === normalized ? null : normalized) + } const handleSearch = (query) => { - setSearchQuery(query); - }; + setSearchQuery(query) + } return ( <> -
+

{filteredAndSortedPasswords.length} password - {filteredAndSortedPasswords.length !== 1 ? "s" : ""} + {filteredAndSortedPasswords.length !== 1 ? 's' : ''}

{selectedFilter && (

- (filtered by:{" "} - + (filtered by:{' '} + {selectedFilter} ) @@ -142,21 +142,21 @@ export const PasswordManager = ({ passwords, setPasswords, onSelect }) => {

handleSearch(e.target.value)} /> -
+
- ); -}; + ) +} diff --git a/src/features/setup/SetupVault.jsx b/src/features/setup/SetupVault.jsx index 5881844..d75ca0c 100644 --- a/src/features/setup/SetupVault.jsx +++ b/src/features/setup/SetupVault.jsx @@ -1,127 +1,127 @@ -import React, { useState } from "react"; -import { vaultPass } from "@/src/lib/vaultPass.js"; -import { useGlobalStore } from "@/src/store/globalStore.js"; -import { useShallow } from "zustand/shallow"; -import { Button } from "@/src/components/ui/Button.jsx"; -import { Input } from "@/src/components/ui/Input.jsx"; -import { Divider } from "@/src/components/ui/Divider.jsx"; +import React, { useState } from 'react' +import { vaultPass } from '@/src/lib/vaultPass.js' +import { useGlobalStore } from '@/src/store/globalStore.js' +import { useShallow } from 'zustand/shallow' +import { Button } from '@/src/components/ui/Button.jsx' +import { Input } from '@/src/components/ui/Input.jsx' +import { Divider } from '@/src/components/ui/Divider.jsx' export const SetupVault = () => { - const [inviteCode, setInviteCode] = useState(""); - const [loading, setLoading] = useState(false); + const [inviteCode, setInviteCode] = useState('') + const [loading, setLoading] = useState(false) const { setVaultPassInitialized, setVaultPassInv } = useGlobalStore( useShallow((state) => ({ setVaultPassInv: state.setVaultPassInv, - setVaultPassInitialized: state.setVaultPassInitialized, - })), - ); + setVaultPassInitialized: state.setVaultPassInitialized + })) + ) const handleCreate = async () => { try { - setLoading(true); - const inv = await vaultPass.createInvite(); - await vaultPass.saveInviteCode(inv); - setVaultPassInv(inv); - setVaultPassInitialized(true); + setLoading(true) + const inv = await vaultPass.createInvite() + await vaultPass.saveInviteCode(inv) + setVaultPassInv(inv) + setVaultPassInitialized(true) } catch (err) { - console.error("Create failed:", err); + console.error('Create failed:', err) } finally { - setLoading(false); + setLoading(false) } - }; + } const handleJoin = async () => { - if (!inviteCode.trim()) return; + if (!inviteCode.trim()) return try { - setLoading(true); - await vaultPass.joinInvite(inviteCode.trim()); - setVaultPassInv(inviteCode.trim()); - setVaultPassInitialized(true); + setLoading(true) + await vaultPass.joinInvite(inviteCode.trim()) + setVaultPassInv(inviteCode.trim()) + setVaultPassInitialized(true) } catch (err) { - console.error("Join failed:", err); + console.error('Join failed:', err) } finally { - setLoading(false); + setLoading(false) } - }; + } return (
-
- +
+

P2P

VAULT

-

+

A Peer-to-Peer Password Manager built using React & Autopass

- ); -}; + ) +} const wrapper = { - height: "100vh", - display: "flex", - justifyContent: "center", - alignItems: "center", - background: "var(--bg-primary)", -}; + height: '100vh', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + background: 'var(--bg-primary)' +} const card = { - width: "360px", - padding: "1rem", - background: "var(--bg-secondary)", - display: "flex", - flexDirection: "column", - gap: "0.15rem", - position: "relative", -}; + width: '360px', + padding: '1rem', + background: 'var(--bg-secondary)', + display: 'flex', + flexDirection: 'column', + gap: '0.15rem', + position: 'relative' +} const badge = { - width: "fit-content", - height: "fit-content", - padding: "0.4rem 1rem", - display: "flex", - alignItems: "center", - justifyContent: "center", - background: "var(--text-jetBrains)", - borderRadius: "50px", - color: "#fff", - fontSize: "1.2rem", - position: "absolute", - top: "-20px", - right: "-20px", - transform: "rotate(15deg)", -}; + width: 'fit-content', + height: 'fit-content', + padding: '0.4rem 1rem', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + background: 'var(--text-jetBrains)', + borderRadius: '50px', + color: '#fff', + fontSize: '1.2rem', + position: 'absolute', + top: '-20px', + right: '-20px', + transform: 'rotate(15deg)' +} diff --git a/src/lib/crpyto.js b/src/lib/crpyto.js index 3d43bc1..08f97c1 100644 --- a/src/lib/crpyto.js +++ b/src/lib/crpyto.js @@ -1,82 +1,82 @@ export const encryptString = async (plainText, roomKey) => { - const enc = new TextEncoder(); - const salt = crypto.getRandomValues(new Uint8Array(16)); + const enc = new TextEncoder() + const salt = crypto.getRandomValues(new Uint8Array(16)) const keyMaterial = await crypto.subtle.importKey( - "raw", + 'raw', enc.encode(roomKey), - "PBKDF2", + 'PBKDF2', false, - ["deriveKey"], - ); + ['deriveKey'] + ) const key = await crypto.subtle.deriveKey( { - name: "PBKDF2", + name: 'PBKDF2', salt, iterations: 100000, - hash: "SHA-256", + hash: 'SHA-256' }, keyMaterial, - { name: "AES-GCM", length: 256 }, + { name: 'AES-GCM', length: 256 }, false, - ["encrypt"], - ); + ['encrypt'] + ) - const iv = crypto.getRandomValues(new Uint8Array(12)); + const iv = crypto.getRandomValues(new Uint8Array(12)) const encrypted = await crypto.subtle.encrypt( - { name: "AES-GCM", iv }, + { name: 'AES-GCM', iv }, key, - enc.encode(plainText), - ); + enc.encode(plainText) + ) const combined = new Uint8Array( - salt.length + iv.length + encrypted.byteLength, - ); + salt.length + iv.length + encrypted.byteLength + ) - combined.set(salt, 0); - combined.set(iv, salt.length); - combined.set(new Uint8Array(encrypted), salt.length + iv.length); + combined.set(salt, 0) + combined.set(iv, salt.length) + combined.set(new Uint8Array(encrypted), salt.length + iv.length) - return btoa(String.fromCharCode(...combined)); -}; + return btoa(String.fromCharCode(...combined)) +} export const decryptString = async (encryptedBase64, roomKey) => { - const enc = new TextEncoder(); - const dec = new TextDecoder(); + const enc = new TextEncoder() + const dec = new TextDecoder() - const data = Uint8Array.from(atob(encryptedBase64), (c) => c.charCodeAt(0)); + const data = Uint8Array.from(atob(encryptedBase64), (c) => c.charCodeAt(0)) - const salt = data.slice(0, 16); - const iv = data.slice(16, 28); - const ciphertext = data.slice(28); + const salt = data.slice(0, 16) + const iv = data.slice(16, 28) + const ciphertext = data.slice(28) const keyMaterial = await crypto.subtle.importKey( - "raw", + 'raw', enc.encode(roomKey), - "PBKDF2", + 'PBKDF2', false, - ["deriveKey"], - ); + ['deriveKey'] + ) const key = await crypto.subtle.deriveKey( { - name: "PBKDF2", + name: 'PBKDF2', salt, iterations: 100000, - hash: "SHA-256", + hash: 'SHA-256' }, keyMaterial, - { name: "AES-GCM", length: 256 }, + { name: 'AES-GCM', length: 256 }, false, - ["decrypt"], - ); + ['decrypt'] + ) const decrypted = await crypto.subtle.decrypt( - { name: "AES-GCM", iv }, + { name: 'AES-GCM', iv }, key, - ciphertext, - ); + ciphertext + ) - return dec.decode(decrypted); -}; + return dec.decode(decrypted) +} diff --git a/src/lib/generatePassword.js b/src/lib/generatePassword.js index 3b50f1e..3e4c6f9 100644 --- a/src/lib/generatePassword.js +++ b/src/lib/generatePassword.js @@ -4,31 +4,31 @@ export const generatePassword = (options) => { useLower = true, useUpper = true, useNumbers = true, - useSymbols = false, - } = options; + useSymbols = false + } = options - const lower = "abcdefghijklmnopqrstuvwxyz"; - const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const numbers = "0123456789"; - const symbols = "*$&%"; + const lower = 'abcdefghijklmnopqrstuvwxyz' + const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + const numbers = '0123456789' + const symbols = '*$&%' - let charset = ""; - if (useLower) charset += lower; - if (useUpper) charset += upper; - if (useNumbers) charset += numbers; - if (useSymbols) charset += symbols; + let charset = '' + if (useLower) charset += lower + if (useUpper) charset += upper + if (useNumbers) charset += numbers + if (useSymbols) charset += symbols if (!charset) { - return ""; + return '' } - const array = new Uint32Array(length); - crypto.getRandomValues(array); + const array = new Uint32Array(length) + crypto.getRandomValues(array) - let password = ""; + let password = '' for (let i = 0; i < length; i++) { - password += charset[array[i] % charset.length]; + password += charset[array[i] % charset.length] } - return password; -}; + return password +} diff --git a/src/lib/vaultPass.js b/src/lib/vaultPass.js index f1ceb66..8b0ad1c 100644 --- a/src/lib/vaultPass.js +++ b/src/lib/vaultPass.js @@ -1,165 +1,165 @@ /* global Pear */ -import Autopass from "autopass"; -import Corestore from "corestore"; -import fs from "fs"; +import Autopass from 'autopass' +import Corestore from 'corestore' +import fs from 'fs' -const baseDir = Pear.app.storage + "/store"; -const inviteFiles = Pear.app.storage + "/invite"; +const baseDir = Pear.app.storage + '/store' +const inviteFiles = Pear.app.storage + '/invite' class VaultPass { - constructor(storagePath = baseDir) { - this.storagePath = storagePath; - this.autopass = null; - this.store = null; - this.initializing = null; - this.inviteFile = inviteFiles; - this.inviteCode = null; + constructor (storagePath = baseDir) { + this.storagePath = storagePath + this.autopass = null + this.store = null + this.initializing = null + this.inviteFile = inviteFiles + this.inviteCode = null } - async vaultExists() { - return fs.existsSync(this.inviteFile); + async vaultExists () { + return fs.existsSync(this.inviteFile) } - async resetVault() { + async resetVault () { if (this.autopass) { - await this.autopass.close(); - this.autopass = null; + await this.autopass.close() + this.autopass = null } if (this.store) { - await this.store.close(); - this.store = null; + await this.store.close() + this.store = null } - this.initializing = null; + this.initializing = null if (fs.existsSync(this.storagePath)) { - fs.rmSync(this.storagePath, { recursive: true, force: true }); + fs.rmSync(this.storagePath, { recursive: true, force: true }) } if (fs.existsSync(this.inviteFile)) { - fs.rmSync(this.inviteFile, { force: true }); + fs.rmSync(this.inviteFile, { force: true }) } - const codeFile = Pear.app.storage + "/inviteCode"; + const codeFile = Pear.app.storage + '/inviteCode' if (fs.existsSync(codeFile)) { - fs.rmSync(codeFile, { force: true }); + fs.rmSync(codeFile, { force: true }) } } - async saveInviteCode(code) { - const codeFile = Pear.app.storage + "/inviteCode"; - fs.writeFileSync(codeFile, code); + async saveInviteCode (code) { + const codeFile = Pear.app.storage + '/inviteCode' + fs.writeFileSync(codeFile, code) } - async loadInviteCode() { - const codeFile = Pear.app.storage + "/inviteCode"; + async loadInviteCode () { + const codeFile = Pear.app.storage + '/inviteCode' if (fs.existsSync(codeFile)) { - return fs.readFileSync(codeFile, "utf-8"); + return fs.readFileSync(codeFile, 'utf-8') } - return null; + return null } - async autoSync(callback) { - this.autopass.on("update", callback); + async autoSync (callback) { + this.autopass.on('update', callback) } - async init() { - if (this.autopass) return; + async init () { + if (this.autopass) return if (!this.initializing) { this.initializing = (async () => { - this.store = new Corestore(this.storagePath); - await this.store.ready(); + this.store = new Corestore(this.storagePath) + await this.store.ready() if (fs.existsSync(this.inviteFile)) { this.autopass = new Autopass(this.store, { - valueEncoding: "json", - }); + valueEncoding: 'json' + }) - await this.autopass.ready(); + await this.autopass.ready() } else { this.autopass = new Autopass(this.store, { - valueEncoding: "json", - }); + valueEncoding: 'json' + }) - await this.autopass.ready(); + await this.autopass.ready() - fs.writeFileSync(this.inviteFile, "created"); + fs.writeFileSync(this.inviteFile, 'created') } - })(); + })() } - await this.initializing; + await this.initializing } - async createInvite() { - await this.init(); + async createInvite () { + await this.init() - const inviteCode = this.autopass.createInvite(); - this.inviteCode = inviteCode; - return inviteCode; + const inviteCode = this.autopass.createInvite() + this.inviteCode = inviteCode + return inviteCode } - async joinInvite(inv) { + async joinInvite (inv) { if (this.autopass) { - await this.autopass.close(); - this.autopass = null; + await this.autopass.close() + this.autopass = null } if (this.store) { - await this.store.close(); - this.store = null; + await this.store.close() + this.store = null } - this.store = new Corestore(this.storagePath); - await this.store.ready(); - const pair = Autopass.pair(this.store, inv); - this.autopass = await pair.finished(); - - this.inviteCode = inv; - fs.writeFileSync(this.inviteFile, "joined"); - fs.writeFileSync(Pear.app.storage + "/inviteCode", inv); - return true; + this.store = new Corestore(this.storagePath) + await this.store.ready() + const pair = Autopass.pair(this.store, inv) + this.autopass = await pair.finished() + + this.inviteCode = inv + fs.writeFileSync(this.inviteFile, 'joined') + fs.writeFileSync(Pear.app.storage + '/inviteCode', inv) + return true } - async add(data) { - await this.init(); + async add (data) { + await this.init() const entry = { id: globalThis.crypto?.randomUUID?.() ?? Date.now().toString(), ...data, createdAt: Date.now(), - updatedAt: Date.now(), - }; - await this.autopass.add(entry.id, JSON.stringify(entry)); - return entry; + updatedAt: Date.now() + } + await this.autopass.add(entry.id, JSON.stringify(entry)) + return entry } - async getAll() { - await this.init(); + async getAll () { + await this.init() - const entries = []; + const entries = [] for await (const { value } of this.autopass.list()) { - entries.push(typeof value === "string" ? JSON.parse(value) : value); + entries.push(typeof value === 'string' ? JSON.parse(value) : value) } - return entries; + return entries } - async delete(id) { - await this.init(); - await this.autopass.remove(id); + async delete (id) { + await this.init() + await this.autopass.remove(id) } - async edit(id, newData) { - await this.init(); - const existingRaw = await this.autopass.get(id); - if (!existingRaw) throw new Error("Entry not found"); + async edit (id, newData) { + await this.init() + const existingRaw = await this.autopass.get(id) + if (!existingRaw) throw new Error('Entry not found') const existing = - typeof existingRaw === "string" ? JSON.parse(existingRaw) : existingRaw; + typeof existingRaw === 'string' ? JSON.parse(existingRaw) : existingRaw const updated = { id, ...existing, ...newData, - updatedAt: Date.now(), - }; - console.log("Updated : ", updated); - await this.autopass.add(id, JSON.stringify(updated)); - return updated; + updatedAt: Date.now() + } + console.log('Updated : ', updated) + await this.autopass.add(id, JSON.stringify(updated)) + return updated } } -export const vaultPass = new VaultPass(); +export const vaultPass = new VaultPass() diff --git a/src/store/globalStore.js b/src/store/globalStore.js index 53f696c..fa5b77b 100644 --- a/src/store/globalStore.js +++ b/src/store/globalStore.js @@ -1,4 +1,4 @@ -import { create } from "zustand"; +import { create } from 'zustand' export const useGlobalStore = create((set) => ({ currentOS: process.platform, @@ -7,7 +7,7 @@ export const useGlobalStore = create((set) => ({ isGenerateFormActive: false, isNoteFormActive: false, isAllEnteriesPopupActive: false, - generatedPassword: "", + generatedPassword: '', vaultPassInitialized: false, refreshPasswordsTrigger: 0, refreshNotesTrigger: 0, @@ -22,7 +22,7 @@ export const useGlobalStore = create((set) => ({ set((state) => ({ isGenerateFormActive: !state.isGenerateFormActive })), toggleAllEnteriesPopup: () => set((state) => ({ - isAllEnteriesPopupActive: !state.isAllEnteriesPopupActive, + isAllEnteriesPopupActive: !state.isAllEnteriesPopupActive })), setGeneratedPassword: (value) => set({ generatedPassword: value }), setVaultPassInitialized: (value) => set({ vaultPassInitialized: value }), @@ -30,9 +30,9 @@ export const useGlobalStore = create((set) => ({ setVaultPassInv: (value) => set({ vaultPassInv: value }), triggerRefreshPasswords: () => set((state) => ({ - refreshPasswordsTrigger: state.refreshPasswordsTrigger + 1, + refreshPasswordsTrigger: state.refreshPasswordsTrigger + 1 })), triggerRefreshNotes: () => set((state) => ({ refreshNotesTrigger: state.refreshNotesTrigger + 1 })), - setAllVaultEntries: (entries) => set({ allVaultEntries: entries }), -})); + setAllVaultEntries: (entries) => set({ allVaultEntries: entries }) +})) diff --git a/src/store/optionStore.js b/src/store/optionStore.js index 91b7351..3e48739 100644 --- a/src/store/optionStore.js +++ b/src/store/optionStore.js @@ -1,6 +1,6 @@ -import { create } from "zustand"; +import { create } from 'zustand' export const useOptionStore = create((set) => ({ - mode: "password", - setMode: (value) => set({ mode: value }), -})); + mode: 'password', + setMode: (value) => set({ mode: value }) +})) diff --git a/src/utils/timeAgo.js b/src/utils/timeAgo.js index c355e99..7e59811 100644 --- a/src/utils/timeAgo.js +++ b/src/utils/timeAgo.js @@ -1,24 +1,24 @@ -export function timeAgo(timestamp) { - const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); +export function timeAgo (timestamp) { + const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) - const diff = (timestamp - Date.now()) / 1000; + const diff = (timestamp - Date.now()) / 1000 const divisions = [ - { amount: 60, name: "seconds" }, - { amount: 60, name: "minutes" }, - { amount: 24, name: "hours" }, - { amount: 7, name: "days" }, - { amount: 4.34524, name: "weeks" }, - { amount: 12, name: "months" }, - { amount: Infinity, name: "years" }, - ]; + { amount: 60, name: 'seconds' }, + { amount: 60, name: 'minutes' }, + { amount: 24, name: 'hours' }, + { amount: 7, name: 'days' }, + { amount: 4.34524, name: 'weeks' }, + { amount: 12, name: 'months' }, + { amount: Infinity, name: 'years' } + ] - let duration = diff; + let duration = diff for (let i = 0; i < divisions.length; i++) { if (Math.abs(duration) < divisions[i].amount) { - return rtf.format(Math.round(duration), divisions[i].name); + return rtf.format(Math.round(duration), divisions[i].name) } - duration /= divisions[i].amount; + duration /= divisions[i].amount } }