diff --git a/example/public/secret-image.secret.svg b/example/public/secret-image.secret.svg new file mode 100644 index 0000000..2624af5 --- /dev/null +++ b/example/public/secret-image.secret.svg @@ -0,0 +1,8 @@ + + + + + + + TOP SECRET + diff --git a/example/src/App.tsx b/example/src/App.tsx index 7ebdb45..cd6e67c 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,30 +1,86 @@ -import React, { useState } from "react"; -import { useCryptoLocker } from "vite-plugin-component-locker"; +import React, { useState, useEffect } from "react"; +import { useCryptoLocker, useCryptoAsset } from "vite-plugin-component-locker"; import "./App.css"; // The decrypted data will be a React component type type SecretComponentType = React.ComponentType; export default function App() { - const { unlock, status, decryptedData: SecretComponent, error } = useCryptoLocker(); + const { unlock: unlockComponent, status: compStatus, decryptedData: SecretComponent, error: compError } = useCryptoLocker(); + const { unlock: unlockAsset, status: assetStatus, decryptedUrl: secretImageUrl, error: assetError } = useCryptoAsset(); + const [password, setPassword] = useState(""); + const [isInitializing, setIsInitializing] = useState(true); + + // Auto-unlock if password is in sessionStorage + useEffect(() => { + const savedPassword = sessionStorage.getItem("locker_password"); + if (savedPassword) { + setPassword(savedPassword); + handleUnlock(savedPassword); + } else { + setIsInitializing(false); + } + }, []); + + const handleUnlock = async (pwd: string) => { + try { + // Unlock both component and asset simultaneously + await Promise.all([ + unlockComponent(() => import("./secret-content.secret"), pwd), + unlockAsset("/secret-image.secret.svg", pwd) + ]); + + // Save password to session storage so it survives page reloads + sessionStorage.setItem("locker_password", pwd); + } catch { + // Errors are already caught and managed by the hooks + sessionStorage.removeItem("locker_password"); + } finally { + setIsInitializing(false); + } + }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!password.trim()) return; - - try { - await unlock(() => import("./secret-content.secret"), password); - } catch { - // The error is already caught and managed by useCryptoLocker's state. - } + await handleUnlock(password); }; + const handleLock = () => { + sessionStorage.removeItem("locker_password"); + window.location.reload(); // Quick way to reset all states + }; + + if (isInitializing) { + return Loading session...; + } + + const isUnlocked = compStatus === "success" && assetStatus === "success"; + const error = compError || assetError; + const isLoading = compStatus === "loading" || assetStatus === "loading"; + return ( - {status === "success" && SecretComponent ? ( - + {isUnlocked && SecretComponent ? ( + + + + {secretImageUrl && ( + + Decrypted Static Asset: + + + Download Image + + + )} + + + Lock Again + + ) : ( Enter Password @@ -34,7 +90,7 @@ export default function App() { value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Enter password…" - disabled={status === "loading"} + disabled={isLoading} style={{ padding: "0.5rem", fontSize: "1rem" }} /> @@ -42,10 +98,10 @@ export default function App() { - {status === "loading" ? "Loading…" : "Unlock"} + {isLoading ? "Loading…" : "Unlock"} )}
Decrypted Static Asset: