commit CLAUDE.md

This commit is contained in:
padreug 2025-09-28 03:58:27 +02:00
parent e062dfe2b8
commit 1a5eee57cb

261
CLAUDE.md
View file

@ -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
<!-- ✅ CORRECT: Use model-value and @update:model-value for custom components -->
<FormField v-slot="{ value, handleChange }" name="active">
<FormItem>
<div class="flex items-center space-x-2">
<FormControl>
<Checkbox
:model-value="value"
@update:model-value="handleChange"
:disabled="isCreating"
/>
</FormControl>
<FormLabel>Make product active</FormLabel>
</div>
</FormItem>
</FormField>
<!-- ❌ WRONG: Don't use :checked for custom components -->
<Checkbox
:checked="value"
@update:checked="handleChange"
/>
```
**Key Points:**
- ✅ **Custom Components**: Use `:model-value` and `@update:model-value` for Shadcn/ui components
- ✅ **Native HTML**: Use `:checked` and `@change` only for native `<input type="checkbox">` 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
<!-- Wrong: Manual form handling without vee-validate -->
@ -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
- 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
<!-- CRITICAL: Separate camera input without 'multiple' attribute -->
<!-- Android browsers require separate inputs for proper camera/gallery handling -->
<input
ref="cameraInput"
type="file"
@change.stop.prevent="handleFileSelect"
accept="image/*"
capture="environment"
:disabled="disabled"
tabindex="-1"
hidden
/>
<!-- Gallery input with multiple support -->
<input
ref="galleryInput"
type="file"
@change.stop.prevent="handleFileSelect"
accept="image/*"
:multiple="multiple"
:disabled="disabled"
tabindex="-1"
hidden
/>
```
### **Vue.js Event Handling Best Practices**
**Use Vue Event Modifiers:**
```vue
<!-- ✅ CORRECT: Use Vue event modifiers -->
<input @change.stop.prevent="handleFileSelect" />
<button @click.stop="triggerCameraInput">Camera</button>
<!-- ❌ WRONG: Manual event handling in methods -->
<input @change="handleFileSelect" />
<!-- Then manually calling event.preventDefault() in method -->
```
**Proper Form Submission Handling:**
```vue
<!-- ✅ CORRECT: Use form.handleSubmit (automatically prevents default) -->
<form @submit="onSubmit">
<!-- ❌ WRONG: Manual preventDefault -->
<form @submit.prevent="handleSubmit">
```
### **Android 14/15 Camera Workarounds**
**Non-Standard MIME Type Workaround:**
```html
<!-- Add non-standard MIME type to force camera access -->
<input type="file" accept="image/*,android/allowCamera" capture="environment" />
```
**Plain File Input Fallback:**
```html
<!-- Fallback: Plain file input shows both camera and gallery options -->
<input type="file" accept="image/*" />
```
### **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.