diff --git a/CLAUDE.md b/CLAUDE.md index e8194d8..9e3327e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -342,6 +342,42 @@ const onSubmit = form.handleSubmit(async (values) => { - ✅ **Checkbox arrays**: Use nested FormField pattern for multiple checkbox selection - ✅ **Type safety**: Zod schema provides full TypeScript type safety +**⚠️ CRITICAL: Checkbox Component Binding** + +For Shadcn/ui Checkbox components, you MUST use the correct Vue.js binding pattern: + +```vue + + + +
+ + + + Make product active +
+
+
+ + + +``` + +**Key Points:** +- ✅ **Custom Components**: Use `:model-value` and `@update:model-value` for Shadcn/ui components +- ✅ **Native HTML**: Use `:checked` and `@change` only for native `` elements +- ✅ **Force Re-render**: Use dynamic `:key` if checkbox doesn't reflect initial form values +- ❌ **Don't Mix**: Never mix checked/model-value patterns - they have different behaviors + +**Reference**: [Vue.js Forms Documentation](https://vuejs.org/guide/essentials/forms.html) + **❌ NEVER do this:** ```vue @@ -703,4 +739,227 @@ export function useMyModule() { **Environment:** - Nostr relay configuration via `VITE_NOSTR_RELAYS` environment variable - PWA manifest configured for standalone app experience -- Service worker with automatic updates every hour \ No newline at end of file +- Service worker with automatic updates every hour + +## Mobile Browser File Input & Form Refresh Issues + +### **Problem Overview** + +Mobile browsers (especially Android) have well-documented issues with file inputs that can cause intermittent page refreshes during image upload operations. This is not a bug in our code, but rather a systemic issue with mobile browser memory management and activity lifecycle. + +### **Root Causes** + +1. **Memory-Induced Refreshes**: Android browsers may reload pages after file selection due to memory pressure when the camera or file chooser app is opened +2. **Activity Lifecycle Kills**: Mobile operating systems can kill browser activities in the background during file selection, causing page reloads when the browser activity is restored +3. **Visibility State Changes**: Screen lock/unlock and app switching can trigger visibility changes that affect authentication state evaluation, causing router guards to redirect +4. **Android 14/15 Camera Issues**: Chrome on Android 14/15 has broken camera capture functionality, often triggering gallery first before camera + +### **Defensive Programming Solutions** + +**Implementation Files:** +- `src/modules/base/components/ImageUpload.vue` - Multi-layer form submission prevention +- `src/modules/market/components/CreateProductDialog.vue` - Visibility-based navigation protection + +**1. Multi-Layer Form Submission Prevention:** +```javascript +// DEFENSIVE: Multiple layers of form submission prevention during upload +const forms = document.querySelectorAll('form') +forms.forEach(form => { + // Layer 1: Override onsubmit handler directly + form.onsubmit = (e) => { + e.preventDefault() + e.stopPropagation() + e.stopImmediatePropagation() + return false + } + + // Layer 2: Add capturing event listener as backup + form.addEventListener('submit', preventSubmit, true) +}) +``` + +**2. Window-Level Submit Blocking:** +```javascript +// DEFENSIVE: Add temporary form submit blocker at window level +const submitBlocker = (e: Event) => { + e.preventDefault() + e.stopImmediatePropagation() + return false +} +window.addEventListener('submit', submitBlocker, true) + +// Remove after operation completes +setTimeout(() => { + window.removeEventListener('submit', submitBlocker, true) +}, 500) +``` + +**3. Visibility Change Protection:** +```javascript +// DEFENSIVE: Protect against screen wake/visibility triggered refreshes +const blockVisibilityRefresh = (_event: Event) => { + if (isDialogActive && props.isOpen) { + console.warn('Visibility change detected while form is open', { + visibilityState: document.visibilityState, + isOpen: props.isOpen, + hasData: !!form.values + }) + } +} + +document.addEventListener('visibilitychange', blockVisibilityRefresh) +``` + +**4. BeforeUnload User Confirmation:** +```javascript +// DEFENSIVE: Show user confirmation dialog for navigation attempts +const blockNavigation = (event: BeforeUnloadEvent) => { + if (isDialogActive && props.isOpen) { + event.preventDefault() + event.returnValue = 'You have unsaved changes. Are you sure you want to leave?' + return event.returnValue + } +} +window.addEventListener('beforeunload', blockNavigation) +``` + +**5. Camera Input Separation:** +```vue + + + + + + +``` + +### **Vue.js Event Handling Best Practices** + +**Use Vue Event Modifiers:** +```vue + + + + + + + +``` + +**Proper Form Submission Handling:** +```vue + +
+ + + +``` + +### **Android 14/15 Camera Workarounds** + +**Non-Standard MIME Type Workaround:** +```html + + +``` + +**Plain File Input Fallback:** +```html + + +``` + +### **Industry-Standard Patterns** + +**1. Page Visibility API (Primary Solution):** +```javascript +// Modern browsers: Use Page Visibility API instead of beforeunload +document.addEventListener('visibilitychange', function() { + if (document.visibilityState === 'visible') { + // Resume critical operations, restore connections + resumeOperations() + } else { + // Save state, pause operations for battery conservation + saveStateAndPause() + } +}) +``` + +**2. Conditional BeforeUnload Protection:** +```javascript +// Only add beforeunload listeners when user has unsaved changes +const addFormProtection = (hasUnsavedChanges) => { + if (hasUnsavedChanges) { + window.addEventListener('beforeunload', preventUnload) + } else { + window.removeEventListener('beforeunload', preventUnload) + } +} +``` + +**3. Session Recovery Pattern:** +```javascript +// Save form state on visibility change +document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + localStorage.setItem('formDraft', JSON.stringify(formData)) + } +}) + +// Restore on page load +window.addEventListener('load', () => { + const draft = localStorage.getItem('formDraft') + if (draft) restoreFormData(JSON.parse(draft)) +}) +``` + +### **Testing & Debugging** + +**Reproduction Steps:** +1. Open form with file upload on mobile device +2. Select camera input during image upload operations +3. Turn screen off/on during upload process +4. Switch between apps during file selection +5. Low memory conditions during camera usage + +**Success Indicators:** +- User sees confirmation dialog instead of losing form data +- Console warnings show visibility change detection working +- Form state preservation during app switching +- Camera input properly separates from gallery input + +**Debug Console Messages:** +```javascript +// Look for these defensive programming console messages +console.warn('Form submission blocked during file upload') +console.warn('Visibility change detected while form is open') +``` + +### **Key Takeaways** + +1. **This is a systemic mobile browser issue**, not a bug in our application code +2. **Multi-layer defensive programming** is the industry-standard solution +3. **Page Visibility API** is more reliable than beforeunload events on mobile +4. **User confirmation dialogs** provide the last line of defense against data loss +5. **Separate camera/gallery inputs** are required for proper Android browser support +6. **The defensive measures are working correctly** when users can choose to prevent navigation + +**⚠️ IMPORTANT**: These issues are intermittent by nature. The defensive programming approach ensures that when they do occur, users have the opportunity to save their work instead of losing form data. \ No newline at end of file