Add video upload support for kind 1 notes on community feed#51
Add video upload support for kind 1 notes on community feed#51spe1020 merged 2 commits intozapcooking:mainfrom
Conversation
spe1020
commented
Jan 3, 2026
- Add video upload button with VideoIcon next to image upload
- Implement handleVideoUpload function with 200MB file size limit
- Add video preview thumbnails in composer
- Include video URLs in post content
- Videos automatically render in feed via existing NoteContent component
- Support for common video formats (mp4, webm, mov, avi, mkv)
- Add video upload button with VideoIcon next to image upload - Implement handleVideoUpload function with 200MB file size limit - Add video preview thumbnails in composer - Include video URLs in post content - Videos automatically render in feed via existing NoteContent component - Support for common video formats (mp4, webm, mov, avi, mkv)
There was a problem hiding this comment.
Pull request overview
This PR adds video upload functionality to the community feed composer, enabling users to upload videos alongside the existing image upload capability. The implementation follows a similar pattern to the existing image upload feature with appropriate adjustments for video-specific requirements.
Key changes:
- Added video upload handler with 200MB file size limit (compared to 10MB for images)
- Implemented video preview thumbnails in the composer UI with remove functionality
- Modified post content builder to include both image and video URLs in kind 1 notes
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async function handleVideoUpload(event: Event) { | ||
| const target = event.target as HTMLInputElement; | ||
| if (!target.files || target.files.length === 0) return; | ||
|
|
||
| uploadingVideo = true; | ||
| error = ''; | ||
|
|
||
| try { | ||
| const file = target.files[0]; | ||
|
|
||
| // Validate file type | ||
| if (!file.type.startsWith('video/')) { | ||
| error = 'Please upload a video file'; | ||
| uploadingVideo = false; | ||
| return; | ||
| } | ||
|
|
||
| // Validate file size (max 200MB for videos) | ||
| if (file.size > 200 * 1024 * 1024) { | ||
| error = 'Video must be less than 200MB'; | ||
| uploadingVideo = false; | ||
| return; | ||
| } | ||
|
|
||
| const body = new FormData(); | ||
| body.append('file[]', file); | ||
|
|
||
| const result = await uploadToNostrBuild(body); | ||
|
|
||
| if (result && result.data && result.data[0]?.url) { | ||
| uploadedVideos = [...uploadedVideos, result.data[0].url]; | ||
| } else { | ||
| error = 'Failed to upload video'; | ||
| } | ||
| } catch (err) { | ||
| console.error('Error uploading video:', err); | ||
| error = 'Failed to upload video. Please try again.'; | ||
| } finally { | ||
| uploadingVideo = false; | ||
| // Reset input so same file can be selected again | ||
| if (videoInputEl) { | ||
| videoInputEl.value = ''; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The handleVideoUpload function is nearly identical to handleImageUpload (lines 150-194), with only the file type validation, size limit, and error messages differing. This code duplication makes maintenance harder and increases the risk of bugs if one function is updated but not the other.
Consider extracting a generic handleMediaUpload function that accepts parameters for file type validation, size limit, and the target array to update. This would centralize the upload logic and make the code more maintainable.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* security: bump vite to ^5.4.21 [closes #235 #236] Made-with: Cursor * security: bump @sveltejs/kit [closes #106] Made-with: Cursor * chore: bump @sveltejs/adapter-cloudflare for vite 6 compat Made-with: Cursor * security: bump wrangler [closes #74] Made-with: Cursor * security: bump markdown-it to ^14.1.1 [refs #84 #86 — defensive timeout in follow-up] Made-with: Cursor * security: override js-yaml to patched line [closes #51] Made-with: Cursor * security: bump @sveltejs/adapter-vercel [closes #95] Made-with: Cursor * chore: migrate _routes.json config to adapter-cloudflare 7.x format Made-with: Cursor --------- Co-authored-by: spe1020 <sethsager@Seths-MacBook-Air.local>
* security(dompurify): sanitize DM bubble output [refs #213] Made-with: Cursor * security(dompurify): sanitize mention composer output [refs #213] Made-with: Cursor * security(dompurify): bump dompurify [closes #213 #243 #245 #247] Made-with: Cursor * security(dompurify): add sanitizer regression tests [refs #213 #243 #245 #247] Made-with: Cursor * test(security): fix sanitize regression test typecheck harness Made-with: Cursor * test(security): address copilot review on sanitizer regression tests - Import sanitizeHTML at top of file for the 7 tests that don't need a fresh module evaluation. Down from 8 module re-imports + hook registrations per file run to 2, addressing the hook-accumulation concern raised in PR #371 review. - Preserve original Object.prototype descriptors for tagNameCheck / attributeNameCheck via getOwnPropertyDescriptor and restore them exactly (or Reflect.deleteProperty when originally absent), instead of unconditionally `delete`-ing in finally. - Same pattern for the SSR test's globalThis.window override: capture the full original descriptor and restore it precisely. - Inline rationale on why the prototype-pollution test does NOT need module re-evaluation (dompurify resolves CUSTOM_ELEMENT_HANDLING config at sanitize-call time, not at module init). * security: rewrite pnpm.overrides to use range floors [closes GHSA-2mjp-6q6p-2qxm GHSA-34x7-hfp2-rc4v GHSA-3v7f-55p6-f55p GHSA-4992-7rv2-5pvq GHSA-737v-mqg7-c878 GHSA-83g3-92jg-28cx GHSA-8qm3-746x-r74r GHSA-8qq5-rm4j-mr97 GHSA-9ppj-qmqm-q256 GHSA-cfw5-2vxh-hr84 GHSA-f23m-r3pf-42rh GHSA-f269-vfmq-vjvj GHSA-mwv9-gp5h-frr4 GHSA-qffp-2rhf-9h96 GHSA-qx2v-qp2m-jg93 GHSA-r5fr-rjxr-66jc GHSA-r6q2-hw4h-h46w GHSA-xxjr-mmjv-4gpg] Made-with: Cursor * security(overrides): tighten range floors to caret-bounded majors Address PR #372 copilot review: 1. Open-ended `>=X.Y.Z` floors could allow a future major release to resolve in during a lockfile regen, potentially breaking builds or runtime behavior. Convert all `pnpm.overrides` to `^X.Y.Z` to keep the security floor while pinning to the current major. Resolved versions in pnpm-lock.yaml are unchanged (tar@7.5.13, dompurify@3.4.2, picomatch@4.0.4, minimatch@10.2.5, etc.). 2. Aligns the dompurify override (`^3.4.2`) with the direct devDependency declaration so the lockfile importer specifier and package.json declaration are consistent — addresses the confusing `>=3.4.2` vs `^3.4.2` mismatch flagged on pnpm-lock.yaml. Verified: pnpm install clean, pnpm test 85/85 pass, pnpm run check 0 errors, pnpm run build succeeds, audit shows no regression. * security: bump vite to ^5.4.21 [closes #235 #236] Made-with: Cursor * security: bump @sveltejs/kit [closes #106] Made-with: Cursor * chore: bump @sveltejs/adapter-cloudflare for vite 6 compat Made-with: Cursor * security: bump wrangler [closes #74] Made-with: Cursor * security: bump markdown-it to ^14.1.1 [refs #84 #86 — defensive timeout in follow-up] Made-with: Cursor * security: override js-yaml to patched line [closes #51] Made-with: Cursor * security: bump @sveltejs/adapter-vercel [closes #95] Made-with: Cursor * chore: migrate _routes.json config to adapter-cloudflare 7.x format Made-with: Cursor --------- Co-authored-by: spe1020 <sethsager@Seths-MacBook-Air.local>