feat: add media save guide
This commit is contained in:
@@ -3,8 +3,12 @@ import { DownloadCloudIcon } from "../icons/DownloadCloudIcon";
|
||||
import { useState, type MouseEvent } from "react";
|
||||
import { useI18n } from "../../i18n";
|
||||
import type { Attachment } from "../../types/post";
|
||||
import { downloadAttachment } from "./utils/downloadFile";
|
||||
import { downloadAttachment, pauseActiveVideos } from "./utils/downloadFile";
|
||||
import { formatBytes } from "./utils/formatBytes";
|
||||
import {
|
||||
mediaSaveKindFromAttachment,
|
||||
useSaveToAlbumGuide,
|
||||
} from "../SaveToAlbumGuide";
|
||||
import { useToast } from "../Toast";
|
||||
|
||||
type AttachmentDownloadPillProps = {
|
||||
@@ -40,14 +44,18 @@ export function AttachmentDownloadPill({
|
||||
}: AttachmentDownloadPillProps) {
|
||||
const { t } = useI18n();
|
||||
const { showToast } = useToast();
|
||||
const { showSaveToAlbumGuide } = useSaveToAlbumGuide();
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
|
||||
const handleDownload = async (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
if (isDownloading) return;
|
||||
pauseActiveVideos();
|
||||
setIsDownloading(true);
|
||||
try {
|
||||
await downloadAttachment(postId, attachment.id, attachment.filename);
|
||||
const mediaKind = mediaSaveKindFromAttachment(attachment);
|
||||
if (mediaKind) showSaveToAlbumGuide(mediaKind);
|
||||
} catch {
|
||||
showToast(t("downloadFail"), "error");
|
||||
} finally {
|
||||
@@ -84,6 +92,7 @@ export function AttachmentDownloadPill({
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={handleDownload}
|
||||
disabled={isDownloading}
|
||||
className={`group z-10 inline-flex overflow-hidden rounded-full bg-black/80 ${fontCls} text-white shadow-lg ring-1 ring-inset ring-white/20 backdrop-blur-md transition hover:bg-black/90 disabled:cursor-wait ${className}`}
|
||||
|
||||
@@ -9,11 +9,16 @@ import { filenameWithExtension, splitFilename } from "../utils/filenameDisplay";
|
||||
import { formatBytes } from "../utils/formatBytes";
|
||||
import { postDisplayText } from "../utils/postText";
|
||||
import { CollapsibleText } from "../CollapsibleText";
|
||||
import {
|
||||
mediaSaveKindFromAttachment,
|
||||
useSaveToAlbumGuide,
|
||||
} from "../../SaveToAlbumGuide";
|
||||
import { useToast } from "../../Toast";
|
||||
|
||||
function AttachmentRow({ postId, att }: { postId: string; att: Attachment }) {
|
||||
const { t } = useI18n();
|
||||
const { showToast } = useToast();
|
||||
const { showSaveToAlbumGuide } = useSaveToAlbumGuide();
|
||||
const { Icon, color } = fileIcon({ mime: att.mime, filename: att.filename });
|
||||
const displayFilename = filenameWithExtension(att.filename, att.mime);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
@@ -24,6 +29,8 @@ function AttachmentRow({ postId, att }: { postId: string; att: Attachment }) {
|
||||
setIsDownloading(true);
|
||||
try {
|
||||
await downloadAttachment(postId, att.id, displayFilename);
|
||||
const mediaKind = mediaSaveKindFromAttachment(att);
|
||||
if (mediaKind) showSaveToAlbumGuide(mediaKind);
|
||||
} catch {
|
||||
showToast(t("downloadFail"), "error");
|
||||
} finally {
|
||||
|
||||
@@ -13,13 +13,14 @@ import {
|
||||
import { useVideoPlayer } from "../overlays/VideoPlayer";
|
||||
import { autolink } from "../utils/autolink";
|
||||
import { CollapsibleText } from "../CollapsibleText";
|
||||
import { downloadAttachment } from "../utils/downloadFile";
|
||||
import { downloadAttachment, pauseActiveVideos } from "../utils/downloadFile";
|
||||
import { formatBytes } from "../utils/formatBytes";
|
||||
import { postDisplayText } from "../utils/postText";
|
||||
import {
|
||||
videoMetadataPreviewSource,
|
||||
videoPreviewSource,
|
||||
} from "../utils/videoPreviewSource";
|
||||
import { useSaveToAlbumGuide } from "../../SaveToAlbumGuide";
|
||||
import { useToast } from "../../Toast";
|
||||
|
||||
const MAX_VISIBLE = 4;
|
||||
@@ -167,13 +168,16 @@ function AttachmentListDownloadButton({
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { showToast } = useToast();
|
||||
const { showSaveToAlbumGuide } = useSaveToAlbumGuide();
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (isDownloading) return;
|
||||
pauseActiveVideos();
|
||||
setIsDownloading(true);
|
||||
try {
|
||||
await downloadAttachment(postId, attachment.id, attachment.filename);
|
||||
showSaveToAlbumGuide("video");
|
||||
} catch {
|
||||
showToast(t("downloadFail"), "error");
|
||||
} finally {
|
||||
@@ -184,6 +188,7 @@ function AttachmentListDownloadButton({
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDownload();
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { assetUrl } from "../../../api";
|
||||
|
||||
export function pauseActiveVideos() {
|
||||
document.querySelectorAll("video").forEach((video) => {
|
||||
video.pause();
|
||||
});
|
||||
}
|
||||
|
||||
export function attachmentDownloadUrl(postId: string, attachmentId: string) {
|
||||
return assetUrl(
|
||||
`/api/posts/${encodeURIComponent(postId)}/attachments/${encodeURIComponent(
|
||||
|
||||
Reference in New Issue
Block a user