feat: Improve multiple file download function.

This commit is contained in:
jarvis2f 2024-12-30 13:49:22 +08:00
parent da459e2253
commit 95481b89f0
2 changed files with 94 additions and 14 deletions

View file

@ -13,6 +13,7 @@ import io.vertx.core.http.CookieSameSite;
import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerOptions; import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerResponse; import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.healthchecks.HealthCheckHandler; import io.vertx.ext.healthchecks.HealthCheckHandler;
import io.vertx.ext.healthchecks.HealthChecks; import io.vertx.ext.healthchecks.HealthChecks;
@ -140,6 +141,7 @@ public class HttpVerticle extends AbstractVerticle {
router.get("/file/preview").handler(this::handleFilePreview); router.get("/file/preview").handler(this::handleFilePreview);
router.post("/file/start-download").handler(this::handleFileStartDownload); router.post("/file/start-download").handler(this::handleFileStartDownload);
router.post("/file/start-download-multiple").handler(this::handleFileStartDownloadMultiple);
router.post("/file/cancel-download").handler(this::handleFileCancelDownload); router.post("/file/cancel-download").handler(this::handleFileCancelDownload);
router.post("/file/toggle-pause-download").handler(this::handleFileTogglePauseDownload); router.post("/file/toggle-pause-download").handler(this::handleFileTogglePauseDownload);
router.post("/file/auto-download").handler(this::handleAutoDownload); router.post("/file/auto-download").handler(this::handleAutoDownload);
@ -315,11 +317,11 @@ public class HttpVerticle extends AbstractVerticle {
} }
TelegramVerticle telegramVerticle = telegramVerticleOptional.get(); TelegramVerticle telegramVerticle = telegramVerticleOptional.get();
telegramVerticle.close(true) telegramVerticle.close(true)
.onSuccess(r -> { .onSuccess(r -> {
telegramVerticles.remove(telegramVerticle); telegramVerticles.remove(telegramVerticle);
sessionTelegramVerticles.entrySet().removeIf(e -> e.getValue().equals(telegramVerticle)); sessionTelegramVerticles.entrySet().removeIf(e -> e.getValue().equals(telegramVerticle));
ctx.end(); ctx.end();
}); });
} }
private void handleTelegrams(RoutingContext ctx) { private void handleTelegrams(RoutingContext ctx) {
@ -511,6 +513,36 @@ public class HttpVerticle extends AbstractVerticle {
.onFailure(ctx::fail); .onFailure(ctx::fail);
} }
private void handleFileStartDownloadMultiple(RoutingContext ctx) {
TelegramVerticle telegramVerticle = getTelegramVerticle(ctx);
if (telegramVerticle == null) {
return;
}
JsonObject jsonObject = ctx.body().asJsonObject();
Long chatId = jsonObject.getLong("chatId");
JsonArray files = jsonObject.getJsonArray("files");
if (chatId == null || CollUtil.isEmpty(files)) {
ctx.fail(400);
return;
}
Future.any(files.stream()
.map(f -> {
JsonObject file = (JsonObject) f;
Long messageId = file.getLong("messageId");
Integer fileId = file.getInteger("fileId");
return telegramVerticle.startDownload(chatId, messageId, fileId);
})
.toList())
.onSuccess(ctx::json)
.onFailure(r -> {
log.error(r, "Failed to start download multiple files");
ctx.json(JsonObject.of("error", "Part of the files failed to start download"));
ctx.response().setStatusCode(400).end();
});
}
private void handleFileCancelDownload(RoutingContext ctx) { private void handleFileCancelDownload(RoutingContext ctx) {
TelegramVerticle telegramVerticle = getTelegramVerticle(ctx); TelegramVerticle telegramVerticle = getTelegramVerticle(ctx);
if (telegramVerticle == null) { if (telegramVerticle == null) {

View file

@ -24,6 +24,7 @@ import {
FileAudioIcon, FileAudioIcon,
FileIcon, FileIcon,
ImageIcon, ImageIcon,
LoaderCircle,
LoaderPinwheel, LoaderPinwheel,
VideoIcon, VideoIcon,
} from "lucide-react"; } from "lucide-react";
@ -51,6 +52,8 @@ import FileNotFount from "@/components/file-not-found";
import FileExtra from "@/components/file-extra"; import FileExtra from "@/components/file-extra";
import useIsMobile from "@/hooks/use-is-mobile"; import useIsMobile from "@/hooks/use-is-mobile";
import FileStatus from "@/components/file-status"; import FileStatus from "@/components/file-status";
import useSWRMutation from "swr/mutation";
import { POST } from "@/lib/api";
interface FileListProps { interface FileListProps {
accountId: string; accountId: string;
@ -112,6 +115,28 @@ export function FileList({ accountId, chatId }: FileListProps) {
); );
const { filters, handleFilterChange, isLoading, files, handleLoadMore } = const { filters, handleFilterChange, isLoading, files, handleLoadMore } =
useFiles(accountId, chatId); useFiles(accountId, chatId);
const {
trigger: startDownloadMultiple,
isMutating: startDownloadMultipleMutating,
} = useSWRMutation(
"/file/start-download-multiple",
(
key,
{
arg,
}: {
arg: {
chatId: number;
files: Array<{ messageId: number; fileId: number }>;
};
},
) => POST(key, arg),
{
onSuccess: () => {
setSelectedFiles(new Set());
},
},
);
useEffect(() => { useEffect(() => {
const observer = new IntersectionObserver( const observer = new IntersectionObserver(
@ -182,7 +207,13 @@ export function FileList({ accountId, chatId }: FileListProps) {
if (selectedFiles.size === files.length) { if (selectedFiles.size === files.length) {
setSelectedFiles(new Set()); setSelectedFiles(new Set());
} else { } else {
setSelectedFiles(new Set(files.map((file) => file.id))); setSelectedFiles(
new Set(
files
.filter((file) => file.downloadStatus === "idle")
.map((file) => file.id),
),
);
} }
}; };
@ -296,16 +327,32 @@ export function FileList({ accountId, chatId }: FileListProps) {
<Button <Button
size="sm" size="sm"
onClick={() => { onClick={() => {
//TODO implement download selected void startDownloadMultiple({
chatId: Number(chatId),
files: Array.from(selectedFiles).map((id) => {
const file = files.find((f) => f.id === id);
return {
messageId: file?.messageId ?? 0,
fileId: file?.id ?? 0,
};
}),
});
}} }}
disabled={Array.from(selectedFiles).every( disabled={
(id) => selectedFiles.size === 0 || startDownloadMultipleMutating
files.find((f) => f.id === id)?.downloadStatus === }
"downloading",
)}
> >
<Download className="mr-2 h-4 w-4" /> {startDownloadMultipleMutating ? (
Download Selected <LoaderCircle
className="mr-2 h-4 w-4 animate-spin"
style={{ strokeWidth: "0.8px" }}
/>
) : (
<>
<Download className="mr-2 h-4 w-4" />
Download Selected
</>
)}
</Button> </Button>
</div> </div>
)} )}
@ -350,6 +397,7 @@ export function FileList({ accountId, chatId }: FileListProps) {
<Checkbox <Checkbox
checked={selectedFiles.has(file.id)} checked={selectedFiles.has(file.id)}
onCheckedChange={() => handleSelectFile(file.id)} onCheckedChange={() => handleSelectFile(file.id)}
disabled={file.downloadStatus !== "idle"}
/> />
</TableCell> </TableCell>
{columns.map((col) => {columns.map((col) =>