mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feat-75] support drag and drop for map zip files
This commit is contained in:
@@ -23,6 +23,8 @@ import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.mode
|
||||
import { PlaylistDownloaderService } from "renderer/services/playlist-downloader.service";
|
||||
import { LocalPlaylistFilter, LocalPlaylistFilterPanel } from "./playlists/local-playlist-filter-panel.component";
|
||||
import { noop } from "shared/helpers/function.helpers";
|
||||
import { Dropzone } from "../shared/dropzone.component";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
|
||||
type Props = {
|
||||
readonly version?: BSVersion;
|
||||
@@ -42,6 +44,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
const mapsDownloader = useService(MapsDownloaderService);
|
||||
const playlistsManager = useService(PlaylistsManagerService);
|
||||
const playlistsDownloader = useService(PlaylistDownloaderService);
|
||||
const notifications = useService(NotificationService);
|
||||
|
||||
const t = useTranslation();
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
@@ -101,6 +104,26 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
return playlistsManager.unlinkVersion(version);
|
||||
}
|
||||
|
||||
const handleFileDrop = async (file: File) => {
|
||||
if (file.type !== "application/zip") {
|
||||
notifications.notifyError({
|
||||
title: "notifications.maps.import-map.titles.error",
|
||||
desc: "notifications.maps.import-map.msgs.zip-accept-only"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const localMap = await mapsManager.importMap(file.path, version);
|
||||
if (localMap) {
|
||||
notifications.notifySuccess({
|
||||
title: "notifications.maps.import-map.titles.success",
|
||||
desc: t("notifications.maps.import-map.msgs.success", {
|
||||
mapName: localMap.rawInfo._songName
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const dropDownItems = ((): DropDownItem[] => {
|
||||
if (tabIndex === 0) {
|
||||
return [
|
||||
@@ -175,7 +198,20 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
]}
|
||||
>
|
||||
<InstalledMapsContext.Provider value={mapsContextValue}>
|
||||
<LocalMapsListPanel className="w-full h-full shrink-0" isActive={isActive && tabIndex === 0} ref={mapsRef} version={version} filter={mapFilter} search={search} linkedState={mapsLinkedState} />
|
||||
<Dropzone
|
||||
className="w-full h-full shrink-0"
|
||||
onDrop={event => {
|
||||
handleFileDrop(event.dataTransfer.files[0]);
|
||||
}}
|
||||
overlay={
|
||||
<div className="text-3xl pointer-events-none">
|
||||
Drop map files here 😃
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<LocalMapsListPanel className="w-full h-full shrink-0" isActive={isActive && tabIndex === 0} ref={mapsRef} version={version} filter={mapFilter} search={search} linkedState={mapsLinkedState} />
|
||||
</Dropzone>
|
||||
|
||||
<LocalPlaylistsListPanel className="w-full h-full shrink-0" isActive={isActive && tabIndex === 1} ref={playlistsRef} version={version} linkedState={playlistLinkedState} filter={playlistFilter} search={search}/>
|
||||
</InstalledMapsContext.Provider>
|
||||
</BsContentTabPanel>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import React, { ReactNode, useState } from "react";
|
||||
|
||||
type Props = Readonly<{
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
overlay?: ReactNode;
|
||||
overlayColor?: string;
|
||||
overlayZIndex?: number;
|
||||
onDrop?: (event: React.DragEvent<HTMLDivElement>) => void;
|
||||
}>;
|
||||
|
||||
// Supports drop and drop functionality for files.
|
||||
export function Dropzone({
|
||||
children,
|
||||
className,
|
||||
overlay,
|
||||
overlayColor,
|
||||
overlayZIndex,
|
||||
onDrop
|
||||
}: Props) {
|
||||
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
const normalizeZIndex = (zIndex?: number) => {
|
||||
return (!zIndex || zIndex <= 0) ? 50 : zIndex;
|
||||
}
|
||||
|
||||
const renderFileOverlay = () => {
|
||||
return (
|
||||
<div
|
||||
className="absolute w-full h-full flex items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: overlayColor || "#000000CC",
|
||||
zIndex: normalizeZIndex(overlayZIndex),
|
||||
}}
|
||||
onDrop={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragging(false);
|
||||
if (onDrop) onDrop(event);
|
||||
}}
|
||||
onDragOver={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onDragEnter={event => {
|
||||
// Should consume the event 1 more time to avoid multiple onDragEnter
|
||||
// triggers by the parent div
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onDragLeave={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragging(false);
|
||||
}}
|
||||
>
|
||||
{overlay}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} onDragEnter={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragging(true);
|
||||
}}>
|
||||
{dragging && renderFileOverlay()}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export class MapsDownloaderService {
|
||||
if (this.os.isOffline) {
|
||||
return null;
|
||||
}
|
||||
return this.ipc.sendV2("download-map", { map, version });
|
||||
return this.ipc.sendV2("bs-maps.download-map", { map, version });
|
||||
}
|
||||
|
||||
public async openDownloadMapModal(version?: BSVersion, ownedMaps: BsmLocalMap[] = []): Promise<ModalResponse<void>> {
|
||||
|
||||
@@ -196,6 +196,27 @@ export class MapsManagerService {
|
||||
})
|
||||
}
|
||||
|
||||
public async importMap(path: string, version?: BSVersion): Promise<BsmLocalMap | null> {
|
||||
try {
|
||||
if (!this.progressBar.require()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await lastValueFrom(this.ipcService.sendV2(
|
||||
"bs-maps.import-map",
|
||||
{ path, version }
|
||||
));
|
||||
} catch (error: any) {
|
||||
this.notifications.notifyError({
|
||||
title: "notifications.maps.import-map.titles.error",
|
||||
desc: ["not-found-zip", "invalid-zip"].includes(error?.code)
|
||||
? `notifications.maps.import-map.msgs.${error.code}`
|
||||
: "Unknown"
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public getMapsInfoFromHashs(hashs: string[]): Observable<SongDetails[]> {
|
||||
return this.ipcService.sendV2("get-maps-info-from-cache", hashs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user