Skip to content
This repository was archived by the owner on Sep 19, 2025. It is now read-only.

Commit 734f0c1

Browse files
committed
added screenlock, fixes for PIN forms
1 parent d2b1f51 commit 734f0c1

6 files changed

Lines changed: 528 additions & 33 deletions

File tree

src/frontend/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import PrivacyPolicy from './components/pages/PrivacyPolicy';
1313
import FileReader from './components/pages/FileReader';
1414
import ApplicationSplash from './components/modals/ApplicationSplash';
1515
import { AnimatePresence } from 'framer-motion';
16+
import { AppLockProvider } from './lib/contexts/AppLockContext';
17+
import ScreenLockModal from './components/modals/ScreenLockModal';
1618

1719
// Key for session storage to check if app has been loaded before
1820
const APP_LOADED_KEY = 'cloudnotes-app-loaded';
@@ -57,12 +59,15 @@ function App() {
5759
}, []);
5860

5961
return (
60-
<>
62+
<AppLockProvider>
6163
{/* Splash screen with animation */}
6264
<AnimatePresence>
6365
{isLoading && <ApplicationSplash isOpen={true} />}
6466
</AnimatePresence>
6567

68+
{/* Application Lock Screen Modal */}
69+
<ScreenLockModal />
70+
6671
{/* Render the app regardless of loading state, but it will be hidden behind the splash screen */}
6772
<div className={isLoading ? 'invisible' : 'visible'}>
6873
<Router>
@@ -117,7 +122,7 @@ function App() {
117122
</Routes>
118123
</Router>
119124
</div>
120-
</>
125+
</AppLockProvider>
121126
);
122127
}
123128

src/frontend/components/form-fields/FormOTP.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const FormOTP = forwardRef(function FormOTPComponent(
6565

6666
return () => clearTimeout(timeout);
6767
}
68-
}, [autoFocus]);
68+
}, [autoFocus, inputRef]);
6969

7070
// Reset the form value when form state changes (e.g., form reset)
7171
useEffect(() => {
@@ -127,11 +127,14 @@ const FormOTP = forwardRef(function FormOTPComponent(
127127
}
128128
}, [ref, focusInput]);
129129

130+
// Get the current input value
131+
const value = form.watch(name) || "";
132+
130133
return (
131134
<FormField
132135
control={form.control}
133136
name={name}
134-
render={({ field }) => (
137+
render={({ field, fieldState }) => (
135138
<FormItem className={cn("space-y-2", className)}>
136139
{label && <FormLabel className="mb-1 select-none">{label}</FormLabel>}
137140
<FormControl>
@@ -156,7 +159,10 @@ const FormOTP = forwardRef(function FormOTPComponent(
156159
<InputOTPSlot
157160
key={index}
158161
index={index}
159-
className="h-12 w-12 text-lg border-muted-foreground/40"
162+
className={cn(
163+
"h-12 w-12 text-lg border-muted-foreground/40",
164+
fieldState.error && "border-destructive"
165+
)}
160166
/>
161167
))}
162168
</InputOTPGroup>
@@ -172,7 +178,10 @@ const FormOTP = forwardRef(function FormOTPComponent(
172178
<InputOTPSlot
173179
key={index}
174180
index={index}
175-
className="h-12 w-12 text-lg border-muted-foreground/40"
181+
className={cn(
182+
"h-12 w-12 text-lg border-muted-foreground/40",
183+
fieldState.error && "border-destructive"
184+
)}
176185
/>
177186
) : null;
178187
})}
@@ -186,7 +195,7 @@ const FormOTP = forwardRef(function FormOTPComponent(
186195
)}
187196
</InputOTP>
188197
</FormControl>
189-
<FormMessage className="text-xs mt-16 text-center absolute w-full left-0" />
198+
<FormMessage className="text-xs mt-16 text-center absolute w-full left-0 select-none" />
190199
</FormItem>
191200
)}
192201
/>

src/frontend/components/modals/PINLockModal.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ export interface PINLockModalProps {
1717
onSave: (pin: string | null) => void;
1818
}
1919

20-
// PIN validation schemas with detailed error messages
21-
const pinDigitSchema = z.string().regex(/^\d{4}$/, {
22-
message: "PIN must be exactly 4 digits",
23-
});
20+
// PIN validation schema - with empty validation message
21+
const pinDigitSchema = z.string().regex(/^\d{4}$/, { message: '' });
2422

2523
// Schema for verifying existing PIN
2624
const verifyPinSchema = z.object({
@@ -97,6 +95,30 @@ const PINLockModal: React.FC<PINLockModalProps> = ({
9795
// Create refs for the submit buttons
9896
const submitBtnRef = useRef<HTMLButtonElement>(null);
9997

98+
// Get current PIN values for each form to check if they're complete
99+
const currentVerifyPin = verifyForm.watch('pin') || '';
100+
const isVerifyPinComplete = currentVerifyPin.length === 4;
101+
102+
const currentCreatePin = createForm.watch('pin') || '';
103+
const isCreatePinComplete = currentCreatePin.length === 4;
104+
105+
const currentConfirmPin = confirmForm.watch('pin') || '';
106+
const isConfirmPinComplete = currentConfirmPin.length === 4;
107+
108+
// Helper to get whether the submit button should be disabled based on current stage
109+
const isSubmitDisabled = () => {
110+
switch (stage) {
111+
case "verify":
112+
return !isVerifyPinComplete;
113+
case "create":
114+
return !isCreatePinComplete;
115+
case "confirm":
116+
return !isConfirmPinComplete;
117+
default:
118+
return true;
119+
}
120+
};
121+
100122
// Helper to get the active input ref based on current stage
101123
const getActiveInputRef = () => {
102124
switch (stage) {
@@ -340,15 +362,15 @@ const PINLockModal: React.FC<PINLockModalProps> = ({
340362
isOpen={isOpen}
341363
onClose={onClose}
342364
title={
343-
<div className="flex items-center gap-2">
365+
<div className="flex items-center gap-2 select-none">
344366
<LockIcon size={18} />
345367
<span>PIN Lock</span>
346368
</div>
347369
}
348370
maxWidth="max-w-md"
349371
>
350372
<div className="p-4 space-y-4 overflow-hidden" onKeyDown={handleKeyDown}>
351-
<div className="text-center space-y-2">
373+
<div className="text-center space-y-2 select-none">
352374
<h3 className="text-lg font-semibold">{title()}</h3>
353375
<p className="text-sm text-muted-foreground">{description()}</p>
354376
</div>
@@ -415,7 +437,7 @@ const PINLockModal: React.FC<PINLockModalProps> = ({
415437
onClick={handleBackFromConfirm}
416438
className="w-1/3 mr-2 rounded-full cursor-pointer"
417439
>
418-
Back
440+
<span className="select-none">Back</span>
419441
</Button>
420442
)}
421443

@@ -424,8 +446,9 @@ const PINLockModal: React.FC<PINLockModalProps> = ({
424446
onClick={handleSubmit}
425447
className={`${stage === "confirm" ? "w-2/3" : "w-full"} rounded-full h-10 font-medium transition-all cursor-pointer`}
426448
ref={submitBtnRef}
449+
disabled={isSubmitDisabled()}
427450
>
428-
{buttonText()}
451+
<span className="select-none">{buttonText()}</span>
429452
</Button>
430453
</div>
431454
</div>

0 commit comments

Comments
 (0)