From db57ac843b899d2e6f69136b3f9bd75b47283823 Mon Sep 17 00:00:00 2001 From: qdust41 Date: Mon, 30 Mar 2026 14:56:24 -0400 Subject: [PATCH] Made file uploads display in drafts --- assets/js/index.tsx | 72 ++++++++++++++++++++++++------- lib/mixer/posts/media_uploader.ex | 2 +- lib/mixer/posts/tweet.ex | 4 +- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/assets/js/index.tsx b/assets/js/index.tsx index e3812f6..dcee6cb 100644 --- a/assets/js/index.tsx +++ b/assets/js/index.tsx @@ -75,6 +75,7 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { const [text, setText] = useState(""); const [error, setError] = useState(null); const [pendingFile, setPendingFile] = useState(null); + const [previewUrl, setPreviewUrl] = useState(null); const [mediaId, setMediaId] = useState(null); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(null); @@ -100,6 +101,10 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { setMediaId(null); setPendingFile(null); setUploadError(null); + if (previewUrl) { + URL.revokeObjectURL(previewUrl); + setPreviewUrl(null); + } onSuccess?.(); }, onError: (e: Error) => setError(e.message), @@ -110,7 +115,11 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { if (!file) return; // Reset the input so the same file can be re-selected after removal e.target.value = ""; + // Revoke any previous object URL to avoid memory leaks + if (previewUrl) URL.revokeObjectURL(previewUrl); + const localUrl = URL.createObjectURL(file); setPendingFile(file); + setPreviewUrl(localUrl); setMediaId(null); setUploadError(null); setUploading(true); @@ -120,13 +129,17 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { if ("error" in result) { setUploadError(result.error); setPendingFile(null); + URL.revokeObjectURL(localUrl); + setPreviewUrl(null); } else { setMediaId(result.mediaId); } } function removeAttachment() { + if (previewUrl) URL.revokeObjectURL(previewUrl); setPendingFile(null); + setPreviewUrl(null); setMediaId(null); setUploadError(null); } @@ -173,6 +186,47 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { rows={2} maxLength={MAX + 1} /> + {previewUrl && pendingFile && ( +
+ {/\.(mp4|mov)$/i.test(pendingFile.name) ? ( +
+ )} + {uploadError &&

{uploadError}

} {error &&

{error}

}
@@ -195,24 +249,10 @@ function ComposeTweet({ onSuccess }: { onSuccess?: () => void }) { onChange={handleFileChange} /> {uploading && ( - Uploading… - )} - {pendingFile && !uploading && ( - - {pendingFile.name} - + + {pendingFile?.name} )} - {uploadError && ( - {uploadError} - )}
diff --git a/lib/mixer/posts/media_uploader.ex b/lib/mixer/posts/media_uploader.ex index 2318c39..52122ad 100644 --- a/lib/mixer/posts/media_uploader.ex +++ b/lib/mixer/posts/media_uploader.ex @@ -13,7 +13,7 @@ defmodule Mixer.Posts.MediaUploader do def storage_dir(_version, {_file, scope}), do: "uploads/media/#{scope.id}" def filename(_version, {file, _scope}) do - Path.basename(file.file_name, Path.extname(file.file_name)) + Path.basename(file.file_name) end def s3_object_headers(_version, {file, _scope}) do diff --git a/lib/mixer/posts/tweet.ex b/lib/mixer/posts/tweet.ex index 351a0be..a877131 100644 --- a/lib/mixer/posts/tweet.ex +++ b/lib/mixer/posts/tweet.ex @@ -42,8 +42,8 @@ defmodule Mixer.Posts.Tweet do Ash.Changeset.after_action(changeset, fn _changeset, tweet -> Mixer.Posts.Media |> Ash.get!(media_id, authorize?: false) - |> Ash.Changeset.for_update(:link_to_tweet, %{tweet_id: tweet.id}) - |> Ash.update!(actor: context.actor) + |> Ash.Changeset.for_update(:link_to_tweet, %{tweet_id: tweet.id}, actor: context.actor) + |> Ash.update!() {:ok, tweet} end)