OpusCapita / filemanager

React based FileManager for browser ( + FS REST API for Node.js and Express)
https://demo.core.dev.opuscapita.com/filemanager/master/?currentComponentName=FileManager&maxContainerWidth=100%25&showSidebar=false
Apache License 2.0
565 stars 122 forks source link

How can I implement search in the client side? #273

Open aleGuardiola opened 4 years ago

aleGuardiola commented 4 years ago

Hi, I cannot find any documentation to implement search functionality, I see a search endpoint in the api documentation but I don't know how to call this endpoint from the react client, can somebody light me or point me where I can find documentation for this feature.

visortelle commented 4 years ago

Hi, @aleGuardiola. As I know, the search functionality isn't implemented yet on the client-side.

PRs are always welcome. 🙂

aleGuardiola commented 4 years ago

Ok cool, I am thinking of a fast work around because I need this for today, I implemented the server in asp.net core and it is working very good, so I am thinking to include a search text as a query parameter in the get children request. Any other ideas, I am down to create a pull request too.

visortelle commented 4 years ago

To create a button with a simple search input at the client-side, you need to implement a new capability in your project.

Example here:

https://github.com/OpusCapita/filemanager/blob/f0afdf1e8038c469ae1176908d8f23b73949a00b/packages/connector-node-v1/src/capabilities/rename.js#L75

The handler function called on capability call.

https://github.com/OpusCapita/filemanager/blob/f0afdf1e8038c469ae1176908d8f23b73949a00b/packages/connector-node-v1/src/capabilities/rename.js#L9

In case if the capability is available in the toolbar context, it will be called on the search button click in the toolbar. https://github.com/OpusCapita/filemanager/blob/f0afdf1e8038c469ae1176908d8f23b73949a00b/packages/connector-node-v1/src/capabilities/rename.js#L89

Pay attention to showDialog function.

https://github.com/OpusCapita/filemanager/blob/f0afdf1e8038c469ae1176908d8f23b73949a00b/packages/connector-node-v1/src/capabilities/rename.js#L72

You can pass any valid React element to this function.

Then replace:

// then replace this part from docs example 
...
capabilities={connectorNodeV1.capabilities}
...
import searchCapability from './my-search-capability';

// to something similar
capabilities={{ ...connectorNodeV1.capabilities, searchCapability }}
aleGuardiola commented 4 years ago

Ok got it, I will follow that example thanks!!

aleGuardiola commented 4 years ago

@visortelle I am done with the UI, by any chance there is any action or any way to set the items in the ListView of the FileNavigator, because the only function I see is "navigateToDir".

visortelle commented 4 years ago

If I understand you right, now you can receive a list of found files from the server?

You can display them as a list of clickable paths or something like this.

Then you also should be able to get a directory id from the server.

Then pass it to navigateToDir function.

Not sure about setSelection function to highlight the needed file if needed. It looks like its missing.

aleGuardiola commented 4 years ago

Yeah I am doing something like that, so basically I am generating a custom id specific for search that has the path of the directory and the search term encoded as base 64, and I am using navigateToDir. So far is working good but I should do it directly on the connector.

visortelle commented 4 years ago

Can you please share your capability's code?

aleGuardiola commented 4 years ago

This is my capability

const searchPrefix = 'ContainerSearch';
const separator = '*';

const label = 'search';

function handler(apiOptions, actions) {
  const {
    showDialog,
    hideDialog,
    navigateToDir,
    updateNotifications,
    getSelectedResources,
    getResourceLocation,
    getResource,
    getNotifications
  } = actions;

  const getCurrentFolder = () => getResource();
  const getCurrentFolderName = () => {
    const currentFolder = getCurrentFolder();
    return !currentFolder.name ? currentFolder.id : currentFolder.name;
  }

  console.log(actions);

//   const getMessage = getMess.bind(null, apiOptions.locale);
  const localeLabel = 'Search'//getMessage(label);

  const rawDialogElement = {
    elementType: 'SetNameDialog',
    elementProps: {
      initialValue: '',
      onHide: hideDialog,
      onSubmit: async (term) => {           
        const currentFolder = getCurrentFolder();         

        const currentFolderSplit = atob( currentFolder.id ).split(separator);

        const id = btoa([searchPrefix, currentFolderSplit[0], currentFolderSplit[1], term].join(separator));

        hideDialog();
        navigateToDir(id);
      },
      onValidate: async (term) => {
        if(!term || term == '') {
            return 'Search Term cannot be empty';
        } else if (term.length >= 25) {
            return 'Search Term cannot be greater than 25 letters.'
        }
        return null;
      },
      inputLabelText: 'Search Term',// getMessage('newName'),
      headerText: 'Search in ' + getCurrentFolderName(),// getMessage('rename'),
      submitButtonText: 'Search',//localeLabel,
      cancelButtonText: 'Cancel'// getMessage('cancel')
    }
  };

  showDialog(rawDialogElement);
}

export default (apiOptions, actions) => {
     return {
    id: label,
    icon: { svg: '<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32px" height="32px"><path d="M 19 3 C 13.488281 3 9 7.488281 9 13 C 9 15.394531 9.839844 17.589844 11.25 19.3125 L 3.28125 27.28125 L 4.71875 28.71875 L 12.6875 20.75 C 14.410156 22.160156 16.605469 23 19 23 C 24.511719 23 29 18.511719 29 13 C 29 7.488281 24.511719 3 19 3 Z M 19 5 C 23.429688 5 27 8.570313 27 13 C 27 17.429688 23.429688 21 19 21 C 14.570313 21 11 17.429688 11 13 C 11 8.570313 14.570313 5 19 5 Z"/></svg>' },
    label: 'Search',
    shouldBeAvailable: (apiOptions) => true,
    availableInContexts: ['files-view', 'toolbar'],//['row', 'toolbar'],
    handler: () => handler(apiOptions, actions)
  };
}
visortelle commented 4 years ago

I don't see any requests to the server's API in your code.

You should use this endpoint: https://demo.core.dev.opuscapita.com/filemanager/master/api/docs/#files__id__search_get

aleGuardiola commented 4 years ago

Yeah I know there is already an api endpoint defined but because I did not find any action method to show the result in the FileNavigator, I did this as a work around. So basically I am telling it to navigate to an id where the id is basically a base 64 string with the search term and the path encoded so the server automatically knows what to do with this id. This is working fine for me. What do you suggest to show the search result? Maybe a Dialog with a ListView, the advantage of what I did is that it stores it as history too so you can go back. But there is also another problems like pagination, streaming the data etc.

aleGuardiola commented 4 years ago

I think the concept of searching and even the api endpoint definition has to be refined, like we need to add a push mechanism to stream the data, in my case it is a lot of data so it will be nice to show the data little by little.

visortelle commented 4 years ago

You are right. <ListView /> is a good candidate to display this kind of data.

Pagination, streaming, and push mechanism sounds like quite a big part of work.

Unfortunately, I don't' have much time to deal with it right now.

If you want, you can dive into it.