bemusic/bemuse

Allow selecting BMS package without drag-and-drop on mobile devices

dtinth opened this issue · 9 comments

Background

Users on mobile devices (Android) cannot drag BMS packages into Bemuse window, so we should allow users to pick a file on their machine.

Requested by

Task

  • Your PR should change these files, CustomBMS.jsx, CustomBMS.scss and CustomSongsIO.js.

  • Add an option to select a file to the Load Custom BMS modal.

    💁‍♂️ How to reach this modal: Launch Bemuse > Enter Game > Keyboard Mode. Click on Play Custom BMS at the bottom left (desktop) or in the hamburger menu at the top left (mobile)

  • Upon clicking the option, the user will be asked to pick a zip, rar or 7z file. Once a file is selected, it should act as if the file has been dropped into the drop zone.

    💁‍♂️ If you need a zip file for testing, try this one. https://www.dropbox.com/s/hwxjo1l5ibj8bef/BMS_Shuin-422.zip

  • All automated checks should pass.

  • Your solution should work on iOS and Android.

  • Please include a screenshot in your PR to confirm that it is working.

Hacktoberfest

If you would like to work on this task, please write a comment stating your intent. We will then assign the issue to you. To ensure continuity, the issue will be unassigned after 3 days of inactivity — so please keep us updated.

aj-ya commented

Hey! I would like to work on this issue.

@aj-ya Thank you for taking on this issue 🙏. It is yours now.

aj-ya commented

Hey! I seem to have hit a dead end while trying to solve this without changing the existing code.
I tried to add an input:file element inside the dropzone div that triggers the handleDrop function when a file is uploaded,
the handleDrop itself has function calls that need the event to be a dragEvent and have a dataTransfer object. I've been trying to create an event with a dataTransfer object to send the data but after some stumbling, I feel I have found some conclusive evidence that it won't work.
source

It is not possible to create a useful DataTransfer object from script, since DataTransfer objects have a processing and security model that is coordinated by the browser during drag-and-drops.
Therefore synthetic objects wont work because of safety constraints.
Screenshot (43)

@aj-ya Hello, thank you, you are right! Seems like the CustomSongsIO code has been hard-coded to only accept drop events. This has been a mistake in my assumption, my apologies for the confusion.

What I would recommend trying:

  1. Create a new function in CustomSongsIO.js based on handleCustomSongFolderDrop.

    • Maybe name it handleCustomSongFileSelect.
    • Then use it in CustomBMS.jsx.
  2. Instead of a new DndResources(event), create an instance of new CustomSongResources(…) directly, e.g.

    new CustomSongResources({
      getFiles: async () => [{ name: file.name, file }]
    })

You can also change CustomBMS.scss.

aj-ya commented

Thanks for the suggestions,will work on that.

aj-ya commented

Hey! I've been working according to your suggestion,but I cant seem to figure out why my createIO async function doesn't fulfill.
code looks like this.

export function handleCustomSongFileSelect(selectedfile) {
console.log('before')
return createIO(async ({ store, customSongLoader }) => {
console.log('async')
const resources = new CustomSongResources({
getFiles: async () => [ { name: selectedfile.name, file: selectedfile } ],
})
console.log('after')
const initialLog = ['Examining selected items...']
return loadCustomSong(resources, initialLog, { store, customSongLoader })
})
}`

the console prints out only the 'before' .
Screenshot (46).
I tried checking the impure docs, but I'm out of ideas. any suggestions?

@aj-ya Sure, happy to help! You cannot call a function directly — its body won’t run. To make it run, you will have to “connect” it to the UI using connectIO here:

connectIO({
onFileDrop: () => (event) =>
CustomSongsIO.handleCustomSongFolderDrop(event),
onPaste: () => (e) => CustomSongsIO.handleClipboardPaste(e),
loadFromURL: () => (url) => CustomSongsIO.handleCustomSongURLLoad(url),
})

Add this:

    onFileSelect: () => (event) =>
      CustomSongsIO.handleCustomSongFileSelect(event),

Then in the component code, call this.props.onFileSelect and the IO function inside should be run. Let me know if you need any further help. Thanks again!

aj-ya commented

Hey! I think I've successfully fixed the issue, please take a look at the PR and the code too.
Also thanks for all the suggestions.

@aj-ya Thanks, I will review and test by tonight!