Skip to content
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

File upload/link from Nextcloud #120

Open
plague69 opened this issue Aug 18, 2020 · 16 comments
Open

File upload/link from Nextcloud #120

plague69 opened this issue Aug 18, 2020 · 16 comments
Labels
enhancement New feature or request

Comments

@plague69
Copy link

plague69 commented Aug 18, 2020

Would be nice if Nextcloud files system can be tied in somehow.

Auto share link create or better yet embed content from Nextcloud

WebDav?

@gary-kim gary-kim added the enhancement New feature or request label Aug 18, 2020
@Iwios
Copy link

Iwios commented Jan 14, 2023

Hello,
I'm very interesting by this feature.

For me is the possibilities to upload file from Nextcloud.
This is possible ?
Or the core of synapse matrix not allowed this kind of features ?

@Thatoo
Copy link

Thatoo commented Jun 26, 2023

I would even like the "upload file" button (in Element desktop app) to be replaced (hidden or optionally removed) by a "share file from nextcloud" button that would not upload anything to the synapse server but only gives access to a way to copy/paste a nextcloud file's share link in the chat.

This should not be too difficult trick to make in Element code.

@Thatoo
Copy link

Thatoo commented Jun 26, 2023

or [ as said by plague69], better yet embed content from Nextcloud.
However, this would require to add a specific widget to each room.
The work is much more important.
It needs to develop a widget and then to find a way for Element to add this widget to all rooms. I'm not sure this last part would be possible.

@Thatoo
Copy link

Thatoo commented Jun 26, 2023

And of course, ideally, it would be nice if in the file app, the share button could allow to share the file to the Element app also. I guess it is an issue to open in the files app repo.

@Thatoo
Copy link

Thatoo commented Jul 9, 2023

@gary-kim To implement this feature, does it require this work to be done #369 ? and thus, does it require either

@Thatoo
Copy link

Thatoo commented Aug 29, 2023

As it doesn't look like neither Matrix will allow soon subpath neither will Nextcloud accept to add riotchat to rootUrlApps, could we move an other way, not as awesome as the integration Sorunome was working on but still something better than the current option.

Would it be possible to replace the "Element join file" button that only let the user upload a file from his/her computer by a "Nextcloud join file" such as the one in spreed that allow the user to either upload a file from computer or share from Nextcloud (let's forget about other option). If having the choice between the two is too complicated, I'd rather have only a "share from nextcloud" button than the current "upload from computer" button.

image

image

or like in mail app. I like actually more the mail app that gives three choices, upload file from computer, share file from nextcloud or paste a share link to a file

image

@Thatoo
Copy link

Thatoo commented Apr 17, 2024

It could be an integration of Nextcloud picker : https://github.com/nextcloud/picker

@Thatoo
Copy link

Thatoo commented Jun 4, 2024

Here is how it could look like thanks to my work on picker : https://github.com/Thatoo/picker/tree/InternalAndClipboard

image

image

image

And once you click on "Copy file link", it copies the link and close the iframe.

An idea I have to move toward a more efficient tool would be to remove all three button
"Cancel"
"Open file"
"Copy link and close window"

and to make the three options "Read", "Write" and "Internal link" becoming button that directly copy the link and close the window, and even ideally move these three buttons to the first page.

Something lik image

However, I didn't want to be that radical in my proposal so I keep it the way it is working now and just added a new option to get the Internal link and a button to copy link and close window keeping the original button that open the file (I just renamed it to be more comprehensible).

Maybe I'll work on that idea later on.

@Thatoo
Copy link

Thatoo commented Jun 4, 2024

My only issue right now is how to display this nice nextcloud logo like that
image

[edit] I guess, it would even better with this icon
image

So far, I did a javascript trick which is not very nice.
It would be much better to be able to add it when the app compile but I don't know how to do that. Then this icon should display only if activated in riotchat settings (with the url picker).

Here is my javascript trick to add in js/main.js file (not recommended, just for a proof of concept) :

const iframe = document.getElementById('riot-iframe')

if (iframe) {
  const interval = setInterval(() => {
    const titles = iframe.contentWindow.document.querySelector(".mx_RoomSublist_tiles")
    const actions = iframe.contentWindow.document.querySelector(".mx_MessageComposer_actions")

    if (titles) {
      setupClickHandler(iframe, titles)
      clearTimeout(interval)
    }

    if (actions) {
      addButton(iframe.contentWindow.document)
    }
  }, 1000)
}

function setupClickHandler(iframe, titles) {
  const iframeDocument = iframe.contentWindow.document
  titles.addEventListener('click', function(event) {
    const roomTile = event.target.closest('.mx_RoomTile')

    if (roomTile) {
      addButton(iframeDocument)
    }
  })
}

function addButton(iframeDocument) {
  const interval = setInterval(() => {
    const button = iframeDocument.querySelector(".mx_MessageComposer_upload");

    if (button) {
      const newButton = iframeDocument.createElement('a');
      newButton.setAttribute('aria-label', 'Nextcloud Share Link');
      newButton.style.marginLeft = '0';
      newButton.style.display = 'flex';
      newButton.style.alignItems = 'center';
      newButton.style.justifyContent = 'center';

      const svg = iframeDocument.createElementNS('http://www.w3.org/2000/svg', 'svg');
      svg.setAttribute('width', '26');
      svg.setAttribute('height', '26');
      svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');

      const path = iframeDocument.createElementNS('http://www.w3.org/2000/svg', 'path');
      path.setAttribute('d', 'M12.018 6.537c-2.5 0-4.6 1.712-5.241 4.015-.56-1.232-1.793-2.105-3.225-2.105A3.569 3.569 0 0 0 0 12a3.569 3.569 0 0 0 3.552 3.553c1.432 0 2.664-.874 3.224-2.106.641 2.304 2.742 4.016 5.242 4.016 2.487 0 4.576-1.693 5.231-3.977.569 1.21 1.783 2.067 3.198 2.067A3.568 3.568 0 0 0 24 12a3.569 3.569 0 0 0-3.553-3.553c-1.416 0-2.63.858-3.199 2.067-.654-2.284-2.743-3.978-5.23-3.977zm0 2.085c1.878 0 3.378 1.5 3.378 3.378 0 1.878-1.5 3.378-3.378 3.378A3.362 3.362 0 0 1 8.641 12c0-1.878 1.5-3.378 3.377-3.378zm-8.466 1.91c.822 0 1.467.645 1.467 1.468s-.644 1.467-1.467 1.468A1.452 1.452 0 0 1 2.085 12c0-.823.644-1.467 1.467-1.467zm16.895 0c.823 0 1.468.645 1.468 1.468s-.645 1.468-1.468 1.468A1.452 1.452 0 0 1 18.98 12c0-.823.644-1.467 1.467-1.467z');
      path.setAttribute('fill', '#656c76');

      svg.appendChild(path);
      newButton.appendChild(svg);

      newButton.onclick = () => {
        // Create the iframe element
        var pickerFrame = document.createElement("IFRAME");
        pickerFrame.id = "pickerFrame";
        pickerFrame.height = "800"; // set the height
        pickerFrame.width = "600"; // set the width
        pickerFrame.src = "/nextcloud/apps/picker/single-link"; // Set the source URL
        pickerFrame.style.border = 'none'; // Remove the default border
        // Set the iframe styles to position it in the middle and on top
        pickerFrame.style.position = 'fixed';
        pickerFrame.style.top = '50%';
        pickerFrame.style.left = '50%';
        pickerFrame.style.transform = 'translate(-50%, -50%)';
        pickerFrame.style.zIndex = '9999'; // Adjust this value as needed
        // Append the iframe to the document body
        document.body.appendChild(pickerFrame);
//        pickerWindow = window.open('/nextcloud/apps/picker/single-link', 'pickerFrame');
      };

      newButton.addEventListener('mouseover', () => {
        path.setAttribute('fill', '#ebeef2');
      });

      newButton.addEventListener('mouseout', () => {
        path.setAttribute('fill', '#656c76');
      });
      
      button.insertAdjacentElement('afterend', newButton);
      clearInterval(interval);
    }
  }, 100);
}

@Thatoo
Copy link

Thatoo commented Jun 4, 2024

I have been told to use skinning to modify the component from a modified element-web; https://github.com/element-hq/element-web/blob/develop/docs/customisations.md#custom-components but I don't know how to do that.

I asked if it was deprecated and I have been answered :
Yes but you could wire it similarly directly in Webpack
using https://webpack.js.org/plugins/normal-module-replacement-plugin/ which is what customisations uses

I have no idea yet what all that means. Any help would be appreciated.

@gary-kim
Copy link
Owner

gary-kim commented Jun 4, 2024 via email

@Thatoo
Copy link

Thatoo commented Jul 17, 2024

Thanks but I first need to end the job on Picker before making a PR here.
And I'm not good enough at Vue to end the job for now.
If someone wants to help me, I'd be happy to end this job.
I describe where I stuck here : nextcloud/picker#34 (comment)
My code is here : https://github.com/Thatoo/picker/tree/ClipboardOnly

@Thatoo
Copy link

Thatoo commented Aug 20, 2024

@gary-kim Finally my final code is here and I'm happy with it https://github.com/Thatoo/picker/tree/one_page and my picker PR is here nextcloud/picker#47

So if you want to try it, you can git clone https://github.com/Thatoo/picker/tree/one_page into your Nextcloud apps folder, build it and then, here is the code I was using to test it in Element for Nextcloud that you could add to riotchat/js/main.js :

/*! For license information please see main.js.LICENSE.txt */
(()=>{"use strict";const o=(o,t,n)=>{const e=Object.assign({escape:!0},n||{});return"/"!==o.charAt(0)&&(o="/"+o),i=(i=t||{})||{},o.replace(/{([^{}]*)}/g,(function(o,t){const n=i[t];return e.escape?encodeURIComponent("string"==typeof n||"number"==typeof n?n.toString():o):"string"==typeof n||"number"==typeof n?n.toString():o}));var i},t=(t,n,e)=>{var i,a,c;const r=Object.assign({noRewrite:!1},e||{}),l=null!=(i=null==e?void 0:e.baseURL)?i:function(){let o=window._oc_webroot;if(typeof o>"u"){o=location.pathname;const t=o.indexOf("/index.php/");if(-1!==t)o=o.slice(0,t);else{const t=o.indexOf("/",1);o=o.slice(0,t>0?t:void 0)}}return o}();return!0!==(null==(c=null==(a=null==window?void 0:window.OC)?void 0:a.config)?void 0:c.modRewriteWorking)||r.noRewrite?l+"/index.php"+o(t,n,e):l+o(t,n,e)};function n(o,t,n){const e=document.querySelector(`#initial-state-${o}-${t}`);if(null===e){if(void 0!==n)return n;throw new Error(`Could not find initial state ${t} of ${o}`)}try{return JSON.parse(atob(e.value))}catch(n){throw new Error(`Could not parse initial state ${t} of ${o}`)}}document.addEventListener("DOMContentLoaded",(function(){i=document.title,e=document.getElementById("riot-iframe"),window.location.hash||"true"!==n("riotchat","disable_custom_urls")||window.localStorage.getItem("mx_user_id")?e.src=t("/apps/riotchat/riot/")+window.location.hash:(e.src=t("/apps/riotchat/riot/")+"#/login",window.location.hash="#/login"),e.onload=a}));let e,i="";function a(){new MutationObserver(r).observe(e.contentWindow.document.querySelector("title"),{childList:!0,attributes:!0,characterData:!0}),e.contentWindow.onhashchange=c,window.onhashchange=()=>{e.contentWindow.location.hash!==window.location.hash&&(e.contentWindow.location.hash=window.location.hash)},"true"!==n("riotchat","sso_force_iframe")&&(e.contentWindow.localStorage.__proto__.setItem=function(){for(var o=arguments.length,n=new Array(o),i=0;i<o;i++)n[i]=arguments[i];"mx_sso_hs_url"===n[0]&&"#/login"===e.contentWindow.location.hash&&(window.location.href=t("/apps/riotchat/riot/#/login")),window.localStorage.setItem.apply(this,n)})}function c(){window.location.hash!==e.contentWindow.location.hash&&(window.location.hash=e.contentWindow.location.hash)}function r(){document.title=e.contentWindow.document.title+" - "+i}})();
//# sourceMappingURL=main.js.map
const iframe = document.getElementById('riot-iframe')

if (iframe) {
  const interval = setInterval(() => {
    const titles = iframe.contentWindow.document.querySelector(".mx_RoomSublist_tiles")
    const actions = iframe.contentWindow.document.querySelector(".mx_MessageComposer_actions")

    if (titles) {
      setupClickHandler(iframe, titles)
      clearTimeout(interval)
    }

    if (actions) {
      addButton(iframe.contentWindow.document)
    }
  }, 1000)
}

function setupClickHandler(iframe, titles) {
  const iframeDocument = iframe.contentWindow.document
  titles.addEventListener('click', function(event) {
    const roomTile = event.target.closest('.mx_RoomTile')

    if (roomTile) {
      addButton(iframeDocument)
    }
  })
}

function addButton(iframeDocument) {
  const interval = setInterval(() => {
    const button = iframeDocument.querySelector(".mx_MessageComposer_upload");

    if (button) {
      const newButton = iframeDocument.createElement('a');
      newButton.setAttribute('aria-label', 'Nextcloud Share Link');
      newButton.style.marginLeft = '0';
      newButton.style.display = 'flex';
      newButton.style.alignItems = 'center';
      newButton.style.justifyContent = 'center';

      const svg = iframeDocument.createElementNS('http://www.w3.org/2000/svg', 'svg');
      svg.setAttribute('width', '26');
      svg.setAttribute('height', '26');
      svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');

      const path = iframeDocument.createElementNS('http://www.w3.org/2000/svg', 'path');
      path.setAttribute('d', 'M 2.4 1.2 A 2.4 2.4 0 0 0 0 3.6 L 0 20.4 A 2.4 2.4 0 0 0 2.4 22.8 L 21.6 22.8 A 2.4 2.4 0 0 0 24 20.4 L 24 7.2 A 2.4 2.4 0 0 0 21.6 4.8 L 13.692012 4.8 L 10.8 1.9079883 A 2.4 2.4 0 0 0 9.1079883 1.2 L 2.4 1.2 z M 12.015293 9.1495898 C 14.128085 9.1487404 15.902746 10.587886 16.45834 12.528223 C 16.941727 11.501136 17.973094 10.772227 19.176035 10.772227 A 3.0319929 3.0319929 0 0 1 22.194434 13.790625 A 3.0311433 3.0311433 0 0 1 19.176035 16.809023 C 17.973944 16.809023 16.942602 16.080963 16.459219 15.053027 C 15.902774 16.993365 14.128085 18.43166 12.015293 18.43166 C 9.8914562 18.43166 8.1066027 16.97725 7.5620508 15.019922 C 7.0863114 16.066548 6.0396784 16.809023 4.8231445 16.809023 A 3.0319929 3.0319929 0 0 1 1.8055664 13.790625 A 3.0319929 3.0319929 0 0 1 4.8231445 10.772227 C 6.0396784 10.772227 7.0871316 11.51388 7.5628711 12.560508 C 8.107423 10.604028 9.8914562 9.1495898 12.015293 9.1495898 z M 12.015293 10.920879 C 10.420718 10.920879 9.1464258 12.195199 9.1464258 13.790625 A 2.856139 2.856139 0 0 0 12.015293 16.660371 C 13.610721 16.660371 14.885039 15.38605 14.885039 13.790625 C 14.885039 12.195199 13.610721 10.920879 12.015293 10.920879 z M 4.8231445 12.543516 L 4.8231445 12.544336 C 4.1239774 12.544336 3.5768555 13.091459 3.5768555 13.790625 A 1.2335258 1.2335258 0 0 0 4.8231445 15.037734 C 5.5223118 15.036884 6.069375 14.489792 6.069375 13.790625 C 6.069375 13.091459 5.5214622 12.543516 4.8231445 12.543516 z M 19.176035 12.543516 L 19.176035 12.544336 C 18.476868 12.544336 17.929746 13.091459 17.929746 13.790625 A 1.2335258 1.2335258 0 0 0 19.176035 15.037734 C 19.875202 15.037734 20.423145 14.489792 20.423145 13.790625 C 20.423145 13.091459 19.875202 12.543516 19.176035 12.543516 z ');
      path.setAttribute('fill', '#656c76');

      svg.appendChild(path);
      newButton.appendChild(svg);

      newButton.onclick = () => {
        // Create the iframe element
        var pickerFrame = document.createElement("iframe");
        pickerFrame.id = "pickerFrame";
        pickerFrame.height = 800; // set the height
        pickerFrame.width = 800; // set the width
        pickerFrame.src = "/apps/picker/single-link?option=Clipboard"; // Set the source URL
        pickerFrame.style.border = 'none'; // Remove the default border
        // Set the iframe styles to position it in the middle and on top
        pickerFrame.style.position = 'fixed';
        pickerFrame.style.top = '50%';
        pickerFrame.style.left = '50%';
        pickerFrame.style.transform = 'translate(-50%, -50%)';
        pickerFrame.style.zIndex = 9999; // Adjust this value as needed
        // Append the iframe to the document body
        document.body.appendChild(pickerFrame);
//        pickerWindow = window.open('/nextcloud/apps/picker/single-link', 'pickerFrame');
      };

      newButton.addEventListener('mouseover', () => {
        path.setAttribute('fill', '#ebeef2');
      });

      newButton.addEventListener('mouseout', () => {
        path.setAttribute('fill', '#656c76');
      });
      
      button.insertAdjacentElement('afterend', newButton);
      clearInterval(interval);
    }
  }, 100);
}
function closePickerIframe() {
  const pickerFrame = document.getElementById('pickerFrame');
  document.body.removeChild(pickerFrame); // Remove the iframe
}

@Thatoo
Copy link

Thatoo commented Aug 20, 2024

function closePickerIframe() {
  const pickerFrame = document.getElementById('pickerFrame');
  document.body.removeChild(pickerFrame); // Remove the iframe
}

could remain in riotchat/js/main.js file.
However, the way I insert the button isn't good (not secure). The button should be added when building Element Web from source.
The button function is easy, it should simply open an iframe as I wrote it and I suggest an svg icon for it but I really don't know how to add this button at the right place when building Element Web from source.

@Thatoo
Copy link

Thatoo commented Aug 20, 2024

If you know how to add the button, we could either hope my PR get merged and invite Element for Nextcloud users to install also the picker app, or, I could make a PR if you want to include all the code of the picker app within Element for Nextcloud app.

@Thatoo
Copy link

Thatoo commented Oct 8, 2024

Nextcloud Picker app is ready to be used by Element for Nextcloud : https://github.com/nextcloud/picker/releases/tag/v1.0.11
I'm starting a PR (a draft) : #656

@Thatoo Thatoo mentioned this issue Oct 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants