box / box-ui-elements

React Components for Box's Design System and Pluggable Components
https://developer.box.com/docs/box-ui-elements
Other
539 stars 305 forks source link

We are using Box UI element's Content Explorer and Content Preview and are having issues adding a custom share url. #3235

Closed JFOYIL3 closed 1 year ago

JFOYIL3 commented 1 year ago

We are trying to create a custom url for when our users create a share link for files. We are setting it using the responseInterceptor prop in ContentExplorer. So if the user clicks any of those file's share button for the first time, it will be our custom url, but every time after it will not work. We are wondering why the prop has this kind of behavior.

Here is our code:

<IntlProvider locale="en">
    <ContentExplorer
         requestInterceptor={requestIntercept}
         responseInterceptor={responseIntercept}
         rootFolderId={folderId.toString()}
         token={accessToken}
    />
</IntlProvider>
var responseIntercept = (response) => {
    const url = window.location.href.replace("/files", "");
    let Id = response.data.id;
    let shareLink = "";

    if (response.data.type === "file" && response.data.shared_link !== null) {
      shareLink = url + "/file-preview?id=" + Id;
      response.data.shared_link = {
        url: shareLink,
        download_url: null,
        vanity_url: null,
        effective_access: "open",
        vanity_name: null,
        effective_permission: "can_download",
        is_password_enabled: false,
        unshared_at: null,
        download_count: 0,
        preview_count: 0,
        access: "open",
        permissions: {
          can_preview: true,
          can_download: true,
        },
      };
    }

    if (response.data.type === "folder" && response.data.shared_link !== null) {
      shareLink = url + "/folder-preview?id=" + Id;
      response.data.shared_link.url = shareLink;
    }

    return response;
  };

We have a function called responseIntercept that runs when a user clicks the share button which will take the response and change the url to our custom share link. However, the next time the user clicks the share button, the function wont run again. We would need for this function to run every time the share button is clicked.

tjuanitas commented 1 year ago

hey @JFOYIL3, the responseInterceptor prop is used for calling a function when there's a response from a network request. but it's only called if there's a request and response. in this case, the response is cached so subsequent clicks to the "Share" button does not make a new request or call the function

See implementation of getFile: https://github.com/box/box-ui-elements/blob/master/src/api/File.js#L244

JFOYIL3 commented 1 year ago

Thank you for the response! Ok, so is there a way we can uncache the response so that subsequent clicks to the "Share" button can call the function (make a new request)?

tjuanitas commented 1 year ago

I don't think there's a way to uncache the response since it's handled by an internal class: https://github.com/box/box-ui-elements/blob/master/src/utils/Cache.js rather than something like localStorage

it's sort of hacky but what you could try is using a MutationObserver to watch for DOM changes on the share modal component (class="be-modal be-modal-share"). if it observes a new node is added, then replace the input element value with the custom URL. you'll still need to use responseInterceptor to create a mapping:

{
    url1: customURL1,
    url2: customURL2,
    url3: customURL3,
    [...]
}

then in the callback for the MutationObserver, get the current value of the input, check if it exists in the mapping, if it does then replace the URL.

JFOYIL3 commented 1 year ago

MutationObserver seems like the thing we needed, and we almost have our desired output, however, it seems to duplicate the observer when I click share on a new file. So here is what I have for the Mutation Observer:

const targetNode = document.querySelector('.be-modal.be-modal-share');
  if (targetNode) {
    const observer = new MutationObserver((mutationsList) => {
      console.log(mutationsList)
      for (const mutation of mutationsList) {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          console.log(mutation.addedNodes)
          // Do something when the modal is shown
          console.log('Modal shown!');
          $(".be-modal-input-group input").each((index, input) => {
            if(input.value in shareLinks){
              input.value = shareLinks[input.value]
            }
          })
        } else if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
          // Do something when the modal is hidden
          console.log('Modal hidden!');
        }
      }
    });
    observer.observe(targetNode, {childList:true});
  }

When I console.log(mutationsList), I noticed that everytime I click the "Share" button on a new file for the first time, it creates two new observers?

First File Share Click image

Second File Share Click image

Another thing to note is that when I close out of the share modal, the original share link will replace the share link we need before closing. In the grand scheme of things, it doesn't really matter, since the modal is closing and when you click share again, our custom url will be there. I guess it's more of an aesthetic thing we might want to fix.

tjuanitas commented 1 year ago

interesting, so when you click "Share" the callback to the MutationObserver is called twice? if there's a duplicate observer, then I think this code block you shared might be executing twice. Can you add a console.log outside of the MutationObserver callback and verify only one observer is being added?

If multiple observers are added, then I think this code block needs to be higher in the component tree (closer to the root and should only be called when the component is initially mounted)

if there's only one observer added but the callback is being executed twice, then it's possible there's some re-rendering that's triggering the second console.log. in this case, you can try narrowing down the if statement to specifically check addedNodes to confirm if the input element was the node that was added.

when I close out of the share modal, the original share link will replace the share link we need before closing

oof. this is sort of hacky again, but I think you might be able to add another MutationObserver to the input element to watch for changes to its attributes. I think value should be considered an attribute, so if you see this change, you change it right back 😅

JFOYIL3 commented 1 year ago

Thank you for your help, we pretty much have what we need. We do have one small issue with certain files not containing a share link. So for example, when I click share for the first time on a file, it'll be our custom share link. However, if the file doesn't have a share link from box, the mapping wont work, and if you click share again, it will display None in the input box. Is there a way to check if a file has been shared in box on the first call so we're not giving them a share link and telling them that there isn't a share link on the second call.