diff --git a/docs/agents/task-logs/weekly/_completed.md b/docs/agents/task-logs/weekly/_completed.md new file mode 100644 index 00000000..8f08c1f2 --- /dev/null +++ b/docs/agents/task-logs/weekly/_completed.md @@ -0,0 +1,19 @@ +# Weekly Agent Task Log: 2025-02-21 (Jules) + +**Agent:** ui-ux-marketing-expert +**Status:** Completed + +## Execution Summary + +1. **Scheduler Setup:** + * Skipped `nostr-lock.mjs` as the script was missing from the environment. + * Created log directory. + +2. **UI/UX Expert Task:** + * **Audit:** Audited `js/ui/profileModalController.js` and `views/` for hardcoded styles. No significant violations found (clean use of utility classes). + * **Remediation:** Identified `alert()` calls in `js/ui/components/UploadModal.js` as a UX violation (blocking UI). Replaced all instances with `this.showError()` or `this.showSuccess()` to align with the design system. + * **Documentation:** Updated `docs/design-system.md` to explicitly forbid `alert()`/`confirm()` and recommend non-blocking alternatives. + +3. **Verification:** + * Ran `tests/ui/uploadModal-integration.test.mjs`: Passed. + * Ran `npm run lint`: Passed. diff --git a/docs/design-system.md b/docs/design-system.md index 7761c42e..67ec243e 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -181,6 +181,12 @@ Mix in Tailwind utilities for icon spacing (`gap-3`), sizing (`px-lg`, `py-sm`), `.badge` renders an inline pill (`bg-panel`, uppercase text). Opt into semantic variants with `data-variant="info"`, `data-variant="critical"`, or `data-variant="neutral"`. Layer on utilities for icon alignment or truncation. +### Interaction Guidelines + +- **No blocking alerts:** Do not use `window.alert()` or `window.confirm()`. These native modals block the main thread and disrupt the user experience. +- **Preferred pattern:** Use the `showError(message)` and `showSuccess(message)` callbacks provided to controllers, or render inline `.notification-banner` elements for persistent feedback. +- **Complex confirmations:** For destructive actions requiring confirmation, use a dedicated modal or a two-step button state (e.g., "Click to confirm") rather than a native confirm dialog. + ### Messaging & Notification Tokens Use the dedicated messaging tokens when styling chat surfaces and banners so they inherit theme-safe contrast and icon colors: diff --git a/js/ui/components/UploadModal.js b/js/ui/components/UploadModal.js index 905d78bb..52bc1cc4 100644 --- a/js/ui/components/UploadModal.js +++ b/js/ui/components/UploadModal.js @@ -477,12 +477,12 @@ export class UploadModal { if (!file) return; if (!this.storageConfigured) { - alert("Please configure storage before selecting a file."); + this.showError("Please configure storage before selecting a file."); e.target.value = ""; // Clear selection return; } if (!this.isStorageUnlocked) { - alert("Please unlock storage before selecting a file."); + this.showError("Please unlock storage before selecting a file."); e.target.value = ""; return; } @@ -696,7 +696,7 @@ export class UploadModal { if (this.results.magnet) this.results.magnet.value = "Upload Failed"; if (this.results.torrentUrl) this.results.torrentUrl.value = "Upload Failed"; - alert(`Upload failed: ${err.message}`); + this.showError(`Upload failed: ${err.message}`); this.inputs.file.value = ""; // Reset } @@ -798,7 +798,7 @@ export class UploadModal { userLogger.error("Thumbnail upload failed:", err); this.thumbnailUploadState.status = 'error'; this.updateThumbnailProgress(null, "Failed."); - alert("Thumbnail upload failed."); + this.showError("Thumbnail upload failed."); } } @@ -934,7 +934,7 @@ export class UploadModal { ? signer.canSign() : typeof signer?.signEvent === "function"; if (!canSign) { - alert("No signer available to unlock storage."); + this.showError("No signer available to unlock storage."); return; } @@ -942,7 +942,7 @@ export class UploadModal { if (signer?.type === "extension") { const permissionResult = await requestDefaultExtensionPermissions(); if (!permissionResult?.ok) { - alert("Extension permissions are required to unlock storage."); + this.showError("Extension permissions are required to unlock storage."); return; } } @@ -956,7 +956,7 @@ export class UploadModal { await this.loadFromStorage(); } catch (err) { userLogger.error("Unlock failed", err); - alert("Failed to unlock storage: " + err.message); + this.showError("Failed to unlock storage: " + err.message); } finally { if (this.toggles.storageUnlock) { this.toggles.storageUnlock.textContent = "Unlock"; @@ -1012,17 +1012,17 @@ export class UploadModal { // Check upload state if (this.activeSource === "upload") { if (this.videoUploadState.status === 'uploading' || this.thumbnailUploadState.status === 'uploading') { - alert("Please wait for uploads to complete."); + this.showError("Please wait for uploads to complete."); return; } if (this.videoUploadState.status === 'error') { - alert("Video upload failed. Please try again."); + this.showError("Video upload failed. Please try again."); return; } if (this.videoUploadState.status !== 'complete') { // Fallback: If no file selected, maybe they want to submit without a new file? // (Not supported in this simplified modal, assume file required) - alert("Please select a video file and wait for it to upload."); + this.showError("Please select a video file and wait for it to upload."); return; } }