diff --git a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx index edc6aa1..19827f3 100644 --- a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx +++ b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx @@ -1,10 +1,11 @@ import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Progress } from '@/components/ui/progress'; +import { Spinner } from '@/components/ui/spinner.js'; import { Textarea } from '@/components/ui/textarea'; +import useUserStore from '@/stores/UserStore'; import { Clock10Icon, Download, Droplets } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; -import useUserStore from '@/stores/UserStore'; const VideoDownloadModal = ({ nonWatermarkVideosLeft = 0, @@ -15,31 +16,94 @@ const VideoDownloadModal = ({ isExporting, exportProgress, exportStatus, + videoBlob, + videoBlobFilename, }) => { const [showDebug, setShowDebug] = useState(false); const [isPremiumExport, setIsPremiumExport] = useState(false); const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState(null); const [status, setStatus] = useState('start'); // 'start', 'processing', 'complete' + const [downloadState, setDownloadState] = useState('idle'); // 'idle', 'downloading', 'downloaded' const exportStartTime = useRef(null); const lastProgressTime = useRef(null); const lastProgress = useRef(0); - + const { premiumExportRequest, premiumExportComplete } = useUserStore(); + const handleShareOrDownload = async () => { + if (!videoBlob || !videoBlobFilename) { + console.error('No video blob available for sharing/download'); + return; + } + + setDownloadState('downloading'); + + try { + // Check if mobile and supports navigator.share + const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); + const canShare = isMobile && navigator.share && navigator.canShare; + + if (canShare) { + try { + const files = [new File([videoBlob], videoBlobFilename, { type: 'video/mp4' })]; + if (navigator.canShare({ files })) { + await navigator.share({ files }); + } else { + // Fallback to download if sharing files isn't supported + const url = URL.createObjectURL(videoBlob); + const link = document.createElement('a'); + link.href = url; + link.download = videoBlobFilename; + link.click(); + URL.revokeObjectURL(url); + } + } catch (shareError) { + console.log('Share failed, falling back to download:', shareError); + // Fallback to download + const url = URL.createObjectURL(videoBlob); + const link = document.createElement('a'); + link.href = url; + link.download = videoBlobFilename; + link.click(); + URL.revokeObjectURL(url); + } + } else { + // Desktop or unsupported mobile - use download + const url = URL.createObjectURL(videoBlob); + const link = document.createElement('a'); + link.href = url; + link.download = videoBlobFilename; + link.click(); + URL.revokeObjectURL(url); + } + + // Set to downloaded state + setDownloadState('downloaded'); + + // Reset to idle after 1.5 seconds + setTimeout(() => { + setDownloadState('idle'); + }, 1500); + } catch (error) { + console.error('Download/share failed:', error); + setDownloadState('idle'); + } + }; + const handleExportWithoutWatermark = async () => { setIsPremiumExport(true); setEstimatedTimeRemaining(null); - + // Call premiumExportRequest and check response const response = await premiumExportRequest(); - + if (response?.error) { // Halt the process if there's an error setIsPremiumExport(false); return; } - + if (response?.success) { // Continue with export if successful setStatus('processing'); @@ -227,11 +291,7 @@ const VideoDownloadModal = ({
- {isPremiumExport ? ( - - ) : ( - - )} + {isPremiumExport ? : }

Exporting {isPremiumExport ? 'without watermark' : ''}

@@ -273,9 +333,36 @@ const VideoDownloadModal = ({

Export Complete!

Your video has been successfully exported.

- +
+ + +
)} diff --git a/resources/js/modules/editor/partials/canvas/video-editor.jsx b/resources/js/modules/editor/partials/canvas/video-editor.jsx index 1241606..ffe7ee2 100644 --- a/resources/js/modules/editor/partials/canvas/video-editor.jsx +++ b/resources/js/modules/editor/partials/canvas/video-editor.jsx @@ -216,7 +216,7 @@ const VideoEditor = ({ width, height, onOpenTextSidebar }) => { const totalDuration = Math.max(...timelineElements.map((el) => el.startTime + el.duration)); - const { isExporting, exportProgress, exportStatus, ffmpegCommand, copyFFmpegCommand, exportVideo } = useVideoExport({ + const { isExporting, exportProgress, exportStatus, ffmpegCommand, copyFFmpegCommand, exportVideo, videoBlob, videoBlobFilename } = useVideoExport({ timelineElements, dimensions, totalDuration, @@ -620,6 +620,8 @@ const VideoEditor = ({ width, height, onOpenTextSidebar }) => { isExporting={isExporting} exportProgress={exportProgress} exportStatus={exportStatus} + videoBlob={videoBlob} + videoBlobFilename={videoBlobFilename} /> ); diff --git a/resources/js/modules/editor/partials/canvas/video-export.jsx b/resources/js/modules/editor/partials/canvas/video-export.jsx index faf5f16..0744c15 100644 --- a/resources/js/modules/editor/partials/canvas/video-export.jsx +++ b/resources/js/modules/editor/partials/canvas/video-export.jsx @@ -13,6 +13,8 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark const [isExporting, setIsExporting] = useState(false); const [exportProgress, setExportProgress] = useState(0); const [exportStatus, setExportStatus] = useState(''); + const [videoBlob, setVideoBlob] = useState(null); + const [videoBlobFilename, setVideoBlobFilename] = useState(null); useEffect(() => { console.log(JSON.stringify(timelineElements)); @@ -692,43 +694,9 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark const epochTimestamp = Date.now(); const fileName = `memeaigen-${epochTimestamp}.mp4`; - // Check if mobile and supports navigator.share - const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); - const canShare = isMobile && navigator.share && navigator.canShare; - - if (canShare) { - try { - const files = [new File([blob], fileName, { type: "video/mp4" })]; - if (navigator.canShare({ files })) { - await navigator.share({ files }); - } else { - // Fallback to download if sharing files isn't supported - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.click(); - URL.revokeObjectURL(url); - } - } catch (shareError) { - console.log('Share failed, falling back to download:', shareError); - // Fallback to download - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.click(); - URL.revokeObjectURL(url); - } - } else { - // Desktop or unsupported mobile - use download - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.click(); - URL.revokeObjectURL(url); - } + // Store the blob and filename in state instead of auto-downloading + setVideoBlob(blob); + setVideoBlobFilename(fileName); setExportProgress(100); setExportStatus('Complete!'); @@ -763,6 +731,8 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark exportProgress, exportStatus, ffmpegCommand, + videoBlob, + videoBlobFilename, // Functions copyFFmpegCommand,