diff --git a/frontend/public/android-chrome-192x192.png b/frontend/public/android-chrome-192x192.png index 2288e83..25ea52a 100644 Binary files a/frontend/public/android-chrome-192x192.png and b/frontend/public/android-chrome-192x192.png differ diff --git a/frontend/public/android-chrome-512x512.png b/frontend/public/android-chrome-512x512.png index c02a17c..9f239e0 100644 Binary files a/frontend/public/android-chrome-512x512.png and b/frontend/public/android-chrome-512x512.png differ diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png index 13fa10e..3de6bdd 100644 Binary files a/frontend/public/apple-touch-icon.png and b/frontend/public/apple-touch-icon.png differ diff --git a/frontend/public/favicon-16x16.png b/frontend/public/favicon-16x16.png index b339182..2d69d65 100644 Binary files a/frontend/public/favicon-16x16.png and b/frontend/public/favicon-16x16.png differ diff --git a/frontend/public/favicon-32x32.png b/frontend/public/favicon-32x32.png index 1525568..80df492 100644 Binary files a/frontend/public/favicon-32x32.png and b/frontend/public/favicon-32x32.png differ diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico index b33886b..428435d 100644 Binary files a/frontend/public/favicon.ico and b/frontend/public/favicon.ico differ diff --git a/frontend/public/site.webmanifest b/frontend/public/site.webmanifest index 368f680..45dc8a2 100644 --- a/frontend/public/site.webmanifest +++ b/frontend/public/site.webmanifest @@ -1,19 +1 @@ -{ - "name": "Pi. Ku.", - "short_name": "Pi. Ku.", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#d4a24f", - "background_color": "#3b1d13", - "display": "standalone" -} +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bf5cc26..e78401f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -31,7 +31,7 @@ export default function App() { return ( -
+
}> + Pi. Ku. logo ); } diff --git a/frontend/src/components/SplashScreen.tsx b/frontend/src/components/SplashScreen.tsx index 0874d18..2e9b67c 100644 --- a/frontend/src/components/SplashScreen.tsx +++ b/frontend/src/components/SplashScreen.tsx @@ -5,7 +5,7 @@ export default function SplashScreen() { return (
diff --git a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx index c0ac222..ba0c666 100644 --- a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx +++ b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx @@ -66,7 +66,7 @@ export function WelcomeLetterOverlay({ type="button" data-testid="dismiss-welcome-letter-btn" onClick={onComplete} - className="btn btn-accent opacity-80 px-12 shadow-lg" + className="btn btn-base btn-xs btn-wide opacity-80 shadow-lg font-light tracking-wider" > I'll see you diff --git a/frontend/src/components/editor/ComposeCanvas.tsx b/frontend/src/components/editor/ComposeCanvas.tsx index 6284ce5..8ab00e0 100644 --- a/frontend/src/components/editor/ComposeCanvas.tsx +++ b/frontend/src/components/editor/ComposeCanvas.tsx @@ -122,7 +122,7 @@ export function ComposeCanvas({ // re-calculates height based on content and applies the zoom transform const syncViewport = useCallback(() => { if (!(fabricRef.current && wrapperRef.current)) return; - textboxRef.current.initDimensions(); + textboxRef.current?.initDimensions(); const minHeight = initialData?.canvasHeight ?? DEFAULT_LOGICAL_HEIGHT; logicalSizeRef.current.height = measureLogicalContentHeight( @@ -260,17 +260,35 @@ export function ComposeCanvas({ let resizeObserver: ResizeObserver | null = null; let lastWidth = 0; + const getInitialWidth = async () => { + if (!wrapperRef.current) return BASE_WIDTH; + let width = wrapperRef.current.clientWidth; + if (width === 0) { + await new Promise((resolve) => requestAnimationFrame(resolve)); + width = wrapperRef.current?.clientWidth || BASE_WIDTH; + } + return width; + }; + + const initResizeOberver = () => { + if (!wrapperRef.current) return null; + const observer = new ResizeObserver(() => { + const nextWidth = wrapperRef.current?.clientWidth; + if (!nextWidth || nextWidth === lastWidth) return; + lastWidth = nextWidth; + syncViewport(); + }); + observer.observe(wrapperRef.current); + return observer; + }; + const initCanvas = async () => { // HACK: actual font may change the text-width - small ux improvement await document.fonts.ready; if (!(wrapperRef.current && canvasRef.current && isMounted)) return; - let width = wrapperRef.current.clientWidth; - if (width === 0) { - await new Promise((resolve) => requestAnimationFrame(resolve)); - width = wrapperRef.current?.clientWidth || BASE_WIDTH; - } + const width = await getInitialWidth(); // init the fabric instance const canvas = new fabric.Canvas(canvasRef.current, { @@ -301,13 +319,7 @@ export function ComposeCanvas({ // auto window resizing based width lastWidth = wrapperRef.current.clientWidth; - resizeObserver = new ResizeObserver(() => { - const nextWidth = wrapperRef.current?.clientWidth; - if (!nextWidth || nextWidth === lastWidth) return; - lastWidth = nextWidth; - syncViewport(); - }); - resizeObserver.observe(wrapperRef.current!); + resizeObserver = initResizeOberver(); }; initCanvas().then(); diff --git a/frontend/src/components/editor/PostSealModal.tsx b/frontend/src/components/editor/PostSealModal.tsx index 607b4bf..1cf964f 100644 --- a/frontend/src/components/editor/PostSealModal.tsx +++ b/frontend/src/components/editor/PostSealModal.tsx @@ -63,7 +63,11 @@ export function PostSealModal({ type="button" data-testid="view-letter-btn" className="btn btn-primary btn-sm" - onClick={() => navigate(PATHS.read(sealedTargetId!))} + onClick={() => { + if (sealedTargetId) { + navigate(PATHS.read(sealedTargetId)); + } + }} > View letter diff --git a/frontend/src/components/editor/ToolBar.tsx b/frontend/src/components/editor/ToolBar.tsx index 776d715..ef16a4a 100644 --- a/frontend/src/components/editor/ToolBar.tsx +++ b/frontend/src/components/editor/ToolBar.tsx @@ -11,7 +11,7 @@ import { XCircleIcon, } from "@phosphor-icons/react"; import { Modal } from "../ui/Modal"; -import type { CanvasStyle } from "./ComposeCanvas.tsx"; +import type { CanvasStyle } from "./ComposeCanvas"; interface ToolBarProps { onAddImage: () => void; diff --git a/frontend/src/components/login/WelcomeModal.tsx b/frontend/src/components/login/WelcomeModal.tsx index 54fb678..97dd30d 100644 --- a/frontend/src/components/login/WelcomeModal.tsx +++ b/frontend/src/components/login/WelcomeModal.tsx @@ -3,9 +3,9 @@ import { ShieldCheckIcon, WarningIcon, } from "@phosphor-icons/react"; -import Logo from "../Logo.tsx"; +import Logo from "../Logo"; import { Modal } from "../ui/Modal"; -import Saajan from "../ui/Saajan.tsx"; +import Saajan from "../ui/Saajan"; export default function WelcomeModal({ setShowWelcome, @@ -53,7 +53,7 @@ export default function WelcomeModal({ href="https://www.privacyguides.org/en/passwords/" target="_blank" className="link link-primary-content" - rel="noopener" + rel="noopener noreferrer" > password manager {" "} diff --git a/frontend/src/components/ui/Modal.tsx b/frontend/src/components/ui/Modal.tsx index 7d02a33..6849390 100644 --- a/frontend/src/components/ui/Modal.tsx +++ b/frontend/src/components/ui/Modal.tsx @@ -19,7 +19,7 @@ export function Modal({ return (
{onClose && ( diff --git a/frontend/src/config/welcomeLetter.ts b/frontend/src/config/welcomeLetter.ts index f35ed58..ce47413 100644 --- a/frontend/src/config/welcomeLetter.ts +++ b/frontend/src/config/welcomeLetter.ts @@ -1,3 +1,4 @@ +import trainImage from "../assets/screenshots/train.png"; import type { CanvasJSON } from "../components/editor/ComposeCanvas"; export function getWelcomeLetterContent(userName: string): CanvasJSON { @@ -30,7 +31,7 @@ export function getWelcomeLetterContent(userName: string): CanvasJSON { originY: "top", left: 36, top: 36, - width: 608, + width: 720, height: 813.6, fill: "#111e67", stroke: null, @@ -90,12 +91,12 @@ export function getWelcomeLetterContent(userName: string): CanvasJSON { globalCompositeOperation: "source-over", skewX: 0, skewY: 0, - src: "/screenshots/train.png", + src: trainImage, crossOrigin: null, filters: [], }, ], - canvasWidth: 680, + canvasWidth: 700, canvasHeight: 900, }; } diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index ca2b8ca..92b0415 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -32,7 +32,7 @@ export const useAuth = () => { const logout = async () => { try { await api.post(endpoints.LOGOUT); - } catch (_error) { + } catch { } finally { clearAuth(); setMasterKey(null); diff --git a/frontend/src/hooks/useLetters.tsx b/frontend/src/hooks/useLetters.tsx index 154c2d1..0a23dbe 100644 --- a/frontend/src/hooks/useLetters.tsx +++ b/frontend/src/hooks/useLetters.tsx @@ -1,32 +1,16 @@ import { useEffect, useMemo, useState } from "react"; import { api } from "../api/apiClient"; +import type { LetterMetadata, LetterResponseData } from "../api/response"; import { endpoints } from "../config/endpoints"; import { useKeyStore } from "../store/useKeyStore"; import { CryptoUtils } from "../utils/crypto"; -export interface Letter { - public_id: string; - type: "KEPT" | "VAULT" | "SENT"; - status: "DRAFT" | "SEALED" | "BURNED"; - updated_at: string; - sealed_at?: string; - unlock_at: string; - encrypted_metadata: string; - encrypted_content: string; - encrypted_dek: string; -} - -export interface LetterMetadata { - recipient: string; - tags?: string[]; -} - -export interface ProcessedLetter extends Letter { +export interface ProcessedLetter extends LetterResponseData { metadata: LetterMetadata; } async function decryptLettersMetadata( - letters: Letter[], + letters: LetterResponseData[], masterKey: CryptoKey, ): Promise { const cryptoUtils = new CryptoUtils(); @@ -43,7 +27,7 @@ async function decryptLettersMetadata( )) as LetterMetadata; return { ...letter, metadata }; - } catch (_err) { + } catch { return { ...letter, metadata: { recipient: "Encrypted Letter" }, diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 56bbadc..90e6f0e 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -7,7 +7,7 @@ import "@fontsource-variable/playwrite-hr-lijeva/wght.css"; import "@fontsource-variable/jost/wght.css"; import "@fontsource-variable/playfair-display/wght.css"; -import App from "./App.tsx"; +import App from "./App"; const root = document.getElementById("root"); if (root) { diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx index b4412d1..edecbf1 100644 --- a/frontend/src/pages/About.tsx +++ b/frontend/src/pages/About.tsx @@ -25,7 +25,9 @@ import { ReactLenis } from "lenis/react"; import { AnimatePresence, motion, useScroll, useTransform } from "motion/react"; import { useEffect, useRef, useState } from "react"; import stamp from "../assets/envelope/stamp.png"; -import Logo from "../components/Logo.tsx"; +import e2eDiag from "../assets/screenshots/e2e.svg"; +import saajan from "../assets/sf.png"; +import Logo from "../components/Logo"; import { Modal } from "../components/ui/Modal"; import "@fontsource/kavivanar/index.css"; @@ -35,7 +37,7 @@ import "@fontsource/architects-daughter/index.css"; import { useNavigate } from "react-router-dom"; function HorizontalScroll({ children }: { children: React.ReactNode }) { - const ref = useRef(null); + const ref = useRef(null); const { scrollYProgress } = useScroll({ target: ref }); const x = useTransform(scrollYProgress, [0, 1], ["0%", "-50%"]); @@ -166,7 +168,7 @@ function PrivacySection() {
-
+

server see @@ -257,7 +259,7 @@ function SpecsSection() { {" "} for the keys.

-

+

This means, both the{" "} encryption and{" "} decryption runs on @@ -288,8 +290,8 @@ function SpecsSection() { only you—hold the very thing that opens that box,{" "} your password. -

-

+

+
Nothing on the server is readable without your actual password.
@@ -304,14 +306,14 @@ function SpecsSection() { (unless this happens)
-
+
-

+