-
-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat-75] support drag and drop for map zip files #575
base: master
Are you sure you want to change the base?
Conversation
e089bcc
to
9eef153
Compare
log.info(`Importing map "${zipPath}" to "${mapPath}"`); | ||
|
||
const zip = await JSZip.loadAsync(await readFile(zipPath)); | ||
const infoFile = zip.file("Info.dat"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -306,6 +309,85 @@ export class LocalMapsManagerService { | |||
return null; | |||
} | |||
|
|||
public importMaps(zipPaths: string[], version?: BSVersion): Observable<Progression> { | |||
const progress: Progression<string> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should handle zips containing multiple maps, not just one zip per map.
It should be compatible with the export maps
feature of BSM, which creates a single zip file for all the selected maps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to currently verify that the exportMaps zip file is generated by BSM? Something like a metadata
within the zip file.
Also the only problem that I can see here is the zip bomb check I've implemented in processZip. Where there is a limit to the file count and file size. We can add that information in within the metadata I guess to bypass the check and a warn users about downloading other users exportMaps zip files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a metadata file to the generated zip, but users can totally create zips that contain multiple maps on their own for plenty of reasons.
We can maybe split the zip verification to another ipc, and warn the user with a modal if the zip exeed a certain size or a certain amount of maps 🤔
Or we could let the user be responsible for what kind of zips they are importing (I mean, if they want to import 1TB of maps, they should be able to do it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a metadata file to the generated zip, but users can totally create zips that contain multiple maps on their own for plenty of reasons.
Yeah fair point, the metadata would be either useless or redundant in any case. Might just do a simple verification on each folder on depth 1 if they contain the "I/info.dat" file.
We can maybe split the zip verification to another ipc, and warn the user with a modal if the zip exeed a certain size or a certain amount of maps 🤔
Or we could let the user be responsible for what kind of zips they are importing (I mean, if they want to import 1TB of maps, they should be able to do it)
I think the latter is probably the better option since the user should be responsible for what the zip files that are being imported is.
I've implemented that check is because sonarcloud suggested it so might ignore that in the future in any case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think SonarCloud's suggestion is great when it’s for a website, but it doesn’t really apply to BSM
); | ||
} | ||
} | ||
observer.complete(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer placing the .complete()
in the finally
block and add an .error()
in the catch
of the promise.
This way, it will handle all potential errors that are not caught by your try/catch block.
(Even though, I agree, in the current state, unhandled errors are shouldn't possible)
await this.importMap(zipPath, version); | ||
} catch (error: any) { | ||
log.error(`Could not import "${zipPath}"`, error); | ||
this.ipc.send<{map: string, error: string}>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its preferable to remove this ipc.send
cause' it can spam the user if all dropped zips are invalid.
Just handle errors on the frontend as normally and show a warning if the progression does not reach 100%
(This is even truer when this function will handle zips of multiple maps)
private constructor() { | ||
this.ipcService = IpcService.getInstance(); | ||
this.modal = ModalService.getInstance(); | ||
this.progressBar = ProgressBarService.getInstance(); | ||
this.notifications = NotificationService.getInstance(); | ||
this.config = ConfigurationService.getInstance(); | ||
this.linker = VersionFolderLinkerService.getInstance(); | ||
|
||
this.ipcService.watch<{map: BsmLocalMap, version?: BSVersion}>("map-imported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need these watchers.
I will deprecate the watch function in the future.
Using it too much overcomplicates the code; the backend should never send random events like this. It should only send events when a function is called, until the end of the function’s execution.
In your importMaps
, the "bs-maps.import-maps"
should return a Progression<BsmLocalMap>
, and then you can do something like this:
sendV2("bs-maps.import-maps", {...args}).pipe(
tap( progress => importListerners.foreach(listener => listener(version, progress.data))
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got this, might just do this on import-maps
and not on download-maps
. I can do the refactoring on download maps once I get this PR merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me this 👌, there is few more places that still use this function, I will try to remove them all today
}); | ||
|
||
// NOTE: Generic notify call from server side | ||
this.ipcService.watch<{map: string, error: string}>("map-not-imported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, but this should be removed anyway due to the spam of notifications
} | ||
|
||
public removeImportListener(listener: (map: BsmLocalMap, version?: BSVersion) => void): void { | ||
const index = this.importListeners.indexOf(listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what happen if the same callback is added multiple time 🤔
Like:
const cb = () => {};
addImportListener(cb);
addImportListener(cb);
addImportListener(cb);
removeImportListener(cb);
Maybe a Set
of callbacks would work better here instead of an array 🤔
I let you test that 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah might be better to do a Set
in any case, but we'll see if that works.
Actually there was a bug is this code as well, where the listeners weren't removed properly. Not sure if the fix should be here since the PR might take some more time to get merged, but the snippet bugged code is here.
In master currently
local-maps-list-panel.component.tsx
useEffect(() => {
if (isActiveOnce) {
loadMaps();
}
return () => {
setMaps(null);
loadPercent$.next(0);
subs.forEach(s => s.unsubscribe());
mapsDownloader.removeOnMapDownloadedListener(loadMaps); // should not be loadMaps
};
}, [isActiveOnce, version, linked]);
useEffect(() => {
if(isActiveOnce){
// The listener that was being added multiple times
mapsDownloader.addOnMapDownloadedListener((map, targetVersion) => {
if (!equal(targetVersion, version)) {
return;
}
setMaps((maps ? [map, ...maps] : [map]));
});
}
return () => {
mapsDownloader.removeOnMapDownloadedListener(loadMaps); // should not be loadMaps
}
}, [isActiveOnce, version, maps])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀 Yeah I will fix that in on master 😅
@Zagrios Side tangent, seems that the call from |
Yeah you need to handle the error in the frontend as well |
Already did in the latest commit, the notification popup shows in the background when I close the error stack in the renderer so I'm not sure what part I've missed 🤔. bs-manager/src/renderer/services/maps-manager.service.ts Lines 163 to 215 in 1ccaad2
|
The error comes from this line You can check the |
Ahhh gotcha, will amend the last commit so that the PR should be ready for a re-review. |
81615b6
to
ebfe849
Compare
@silentrald I've just pushed some fixes, but I have to go.
The |
@Zagrios might be related to the path separator. Not sure if Windows uses |
fixed issue in map downloads where download listener in MapsDownloaderService was not being removed
Quality Gate failedFailed conditions |
Scope
closes #75
Implementation
Implemented drag and drop for map zip files.
Added
Dropzone
component to support drag and drop of files.Created
processZip
in zip helper file to safely handle zip files, although SonarCloud doesn't seem to like the handling.Added
LocalMapsManagerService.importMap
to import the zip file and copied almost the same code in downloadMap for pathing and caching.Screenshots
How to Test
Drag and drop the following files:
Check if it will be installed in either the specific version folder or shared folder as well.
Check if switching to the playlist tab is not affected by the dropzone.
New Resource Text
List of added resource text
notifications.maps.import-map.titles.success: Map import complete
notifications.maps.import-map.titles.error: An error occurred while importing the map
notifications.maps.import-map.msgs.success: Imported all maps successfully
notifications.maps.import-map.msgs.success: Imported some maps successfully
notifications.maps.import-map.msgs.only-accept-zip: Only zip files are supported
notifications.maps.import-map.msgs.not-found-zip: Zip file does not exists
notifications.maps.import-map.msgs.invalid-zip: Zip file does not contain any dat files
Emoji Guide
For reviewers: Emojis can be added to comments to call out blocking versus non-blocking feedback.
E.g: Praise, minor suggestions, or clarifying questions that don’t block merging the PR.
E.g: Blocking feedback must be addressed before merging.