soscripted / sox

Stack Overflow Extras: a userscript for the Stack Exchange websites to add a bunch of optional toggle-able features
http://stackapps.com/q/6091/
MIT License
72 stars 15 forks source link

Feature to paste images directly into the textarea without needing image dialog #338

Closed GaurangTandon closed 6 years ago

GaurangTandon commented 6 years ago

Similar to what GitHub does. If you copy an image into your clipboard and paste it into the GitHub textbox, it:

  1. automatically detects that the data being pasted is an image
  2. contacts the respective image server and uploads the image there
  3. when the upload is complete, returns the generated URL and embeds the image as ![alt text](URL)

I will try and see if this is possible with the SE Imgur API, and if it is, will submit a pull request with this attempted new feature. Hopefully it will be short enough, and be a useful addition to SOX :)

GaurangTandon commented 6 years ago

This SO question is supposed to help, but with my little AJAX experience, I can't figure it out :/ @shu8 do you have some experience uploading images through AJAX?


Self-notes: Some useful SO questions:

  1. https://stackoverflow.com/questions/6333814/how-does-the-paste-image-from-clipboard-functionality-work-in-gmail-and-google-c#
  2. https://stackoverflow.com/questions/8578136/how-to-read-a-file-on-paste-event-in-html5#
  3. https://stackoverflow.com/questions/48382024/send-file-from-clipboard#

Concerned Web API: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer

shu8 commented 6 years ago

@GaurangTandon sure I'll have a look! I've done something very similar in another project so hopefully it won't be too hard to make it work for SOX!

GaurangTandon commented 6 years ago

Great! I did try this myself first though, and interestingly got the 418 error! :O The error reads: "Sorry, your request could not be completed because it looked suspicious".

The code is pretty short, and I am unable to figure out what's wrong (my code matches well with what I found in those links above)

(function() {
    'use strict';

    window.addEventListener("paste", function(event){
        var node = event.target,
            isTextarea = node.tagName === "TEXTAREA",
            clipboardData = event.clipboardData,
            formData = new FormData(),
            req = new XMLHttpRequest(),
            reader = new FileReader(),
            items = clipboardData && clipboardData.items,
            blob = null;

        if(!items) return true;

        for(var i = 0, len = items.length; i < len; i++)
            if(/^image/.test(items[i].type)){
                blob = items[i].getAsFile();
                break;
            }

        if(isTextarea && blob){
            reader.onload = function(){
                formData.append("file", this.result);

                req.open("POST", "https://stackoverflow.com/upload/image?https=true");
                req.setRequestHeader("accept", "*/*");
                req.setRequestHeader("content-type", "multipart/form-data");
                req.send(formData);

                debugger;

                req.onload = function(e){
                    debugger;
                };
            };

            reader.readAsDataURL(blob);
        }
    });
})();

I'll again have a look later.

Later observations:

  1. My req content-type is missing boundary=----WebKitFormBoundarycWMMfdb8ZwkGyDPY.
  2. My Request Payload (formData) is missing filename="image.png" and Content-Type: image/png at the beginning.
  3. The ending for the Request Payload of the correctly uploaded image is: image while that of the 418 error image is: image Apparently, there's some clear difference in encoding for the same image (the first image has square boxes, latter doesn't).

Ahh, I am missing the CRSF token :( Any idea where I can get one from, @shu8 ? Or should I post a question on StackApps?

shu8 commented 6 years ago

@GaurangTandon interesting, I couldn't get it to work for the Stack Imgur API, but I could get it to work with the normal imgur api by registering an application. I just pushed what I did -- if we can get it to work with stack's imgur it would be great!

to get the fkey I think it is in the #fkey element's value on every page.


Also, something I didn't get whilst I was doing it is why can there be more than one item?

GaurangTandon commented 6 years ago

Also, something I didn't get whilst I was doing it is why can there be more than one item?

Yeah, I noticed a few times the items array was set to ["image/png", "Files"]. Not sure why though :/

I couldn't get it to work for the Stack Imgur API

Hmm, I am not sure if this is a good idea. Stack gives exclusive support to the i.stack domain, taking regular backups of the images, and ensuring their eternal permanence. While I agree that normal imgur images shouldn't vanish either, the probability of the latter is higher. Moreover, I'd personally want to replicate the default functionality as far as possible :)

to get the fkey I think it is in the #fkey element's value on every page.

Nice find! I inserted a simple formData.append("CSRF", document.getElementById("fkey").value);, but that didn't still work though :/


PS: You can set it to $(document).on('paste', 'textarea', function(e) { so that it works on all answer boxes, comment boxes, as well as chat boxes! ;)

shu8 commented 6 years ago

@GaurangTandon I remember seeing a stackapps question on uploading images to the SE imgur account. I'll try finding it!

Sorry - I don't understand, are you saying it's a bad idea to use the SE account, or bad idea to use the normal imgur API?

shu8 commented 6 years ago

@GaurangTandon Found it! Is there an API to upload images to SE's imgur installation?

GaurangTandon commented 6 years ago

are you saying it's a bad idea to use the SE account, or bad idea to use the normal imgur API?

I was saying that it's a bad idea to use the normal imgur API. That question link is great! Wanna give it a shot? (because I have given up entirely now, can't get it to work at all :'()

shu8 commented 6 years ago

Sure, I'll give it another try soon!

On Thu, 28 Jun 2018, 06:24 Gaurang Tandon, notifications@github.com wrote:

are you saying it's a bad idea to use the SE account, or bad idea to use the normal imgur API?

I was saying that it's a bad idea to use the normal imgur API. That question link is great! Wanna give it a shot? (because I have given up entirely now, can't get it to work :'( at all)

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/soscripted/sox/issues/338#issuecomment-400914357, or mute the thread https://github.com/notifications/unsubscribe-auth/AIcNjnc-nuE2Ha3dNdXCt5LtYlBS_rDAks5uBGh7gaJpZM4U5GAS .

shu8 commented 6 years ago

@GaurangTandon I think I got it to work now in dev 2.2.5 -- can you confirm?

the URL extraction is reallllllllly messy, so do you mind having a look to see if you can clean it up? πŸ˜†

GaurangTandon commented 6 years ago

Okay, first off, I spent five minutes debugging why the paste event doesn't fire when I realized the setting "Paste images directly into the post editor without the image dialog" is unchecked by default!! :O Please check it like all others.

Then, a couple more I even converted the first few lines to:

document.addEventListener('paste', function(e) {
    var node = e.target,
         isTextarea = node && node.tagName === "TEXTAREA";

to make sure it works on comment boxes. But it still doesn't work. That's because clipboard.items isn't what you think it is always. Here's an example. If you right click on a SE profile picture, press "Copy image", and paste it into the textbox, you'll get this:

image

And since we need the .getAsFile() method (that is only present in .items values), we would need the loop (that was there in my original code) in here.

Also, e.originalEvent errors in Chrome 67.


Still, after all that, I get an xhr error :(

image

I've made a commit with fixes for all of the stuff above, but I don't know how to fix the xhr :/

the URL extraction is reallllllllly messy, so do you mind having a look to see if you can clean it up?

Once you fix the xhr request, I can surely look into ways to optimize it :)

shu8 commented 6 years ago

@GaurangTandon cool thanks! I still don't understand why the whole multiple items happens, but your fix seems perfect!

The POST error was to do with me being an idiot and only testing on Meta, so I forgot to change the URL to the /upload/image path of the current site!

Hopefully it should work now in dev v2.2.6!

GaurangTandon commented 6 years ago

Thanks for the collaboration, I'm glad we finally got it to work :D

One thing I still wonder though is how are you going to tell the existing users that there's a new feature? The new users might notice this feature while configuring their settings, but I doubt the old users would notice it :/

shu8 commented 6 years ago

@GaurangTandon thanks! nice touch with the pre-selection too! (and using just '/upload/image?https=true' is pure genius πŸ˜†).

You're right. In the past, we've never really done anything but I've been thinking recently to add a new 'added_in_version' property to the sox.features.info.json file, and with new releases, the script would show a small popup to users showing new features for the version -- thoughts?

GaurangTandon commented 6 years ago

nice touch with the pre-selection too! (and using just '/upload/image?https=true' is pure genius πŸ˜†).

I just found that literally a day ago while google searching :P

I've been thinking recently to add a new 'added_in_version' property to the sox.features.info.json

that would be too much of a burden. Instead, you could simply keep updating the contents of the popup manually. There would be two types of users: a) old and b) new. Is there a way to detect which ones are old (and are only updating their userscript) and which ones are new (and are actually installing the userscript)? If that is possible then we could only show the popup to the old users and we're done :D

shu8 commented 6 years ago

Hmm, I'll look into that more. If there's not an out-of-the-box way, a simple solution would be to store a GM setting lastVersion and compare it to the current version (which is easily obtainable), setting it to the current version every time it turns out to be different.

On Fri, 29 Jun 2018 at 18:08, Gaurang Tandon notifications@github.com wrote:

nice touch with the pre-selection too! (and using just '/upload/image?https=true' is pure genius πŸ˜†).

I just found that literally a day ago while google searching :P

I've been thinking recently to add a new 'added_in_version' property to the sox.features.info.json

that would be too much of a burden. You could simply keep updating the contents of the popup manually. There would be two types of users: a) old and b) new. Is there a way to detect which ones are old (and are only updating their userscript) and which ones are new (and are actually installing the userscript)? If that is possible then we could only show the popup to the old users and we're done :D

β€” You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/soscripted/sox/issues/338#issuecomment-401416364, or mute the thread https://github.com/notifications/unsubscribe-auth/AIcNjinfkqxfJdhBzjq0M5tiMq0u0VVWks5uBl76gaJpZM4U5GAS .

GaurangTandon commented 6 years ago

Not sure if that would work, considering that currently both old and new users don't have that lastVersion property.

If there's not an out of the box available to us, our best method would be to first ship the lastVersion property and only after that ship any new feature.

On Sat 30 Jun, 2018, 01:00 α”•α–Ία˜Žα•Š, notifications@github.com wrote:

Hmm, I'll look into that more. If there's not an out-of-the-box way, a simple solution would be to store a GM setting lastVersion and compare it to the current version (which is easily obtainable), setting it to the current version every time it turns out to be different.

On Fri, 29 Jun 2018 at 18:08, Gaurang Tandon notifications@github.com wrote:

nice touch with the pre-selection too! (and using just '/upload/image?https=true' is pure genius πŸ˜†).

I just found that literally a day ago while google searching :P

I've been thinking recently to add a new 'added_in_version' property to the sox.features.info.json

that would be too much of a burden. You could simply keep updating the contents of the popup manually. There would be two types of users: a) old and b) new. Is there a way to detect which ones are old (and are only updating their userscript) and which ones are new (and are actually installing the userscript)? If that is possible then we could only show the popup to the old users and we're done :D

β€” You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/soscripted/sox/issues/338#issuecomment-401416364, or mute the thread < https://github.com/notifications/unsubscribe-auth/AIcNjinfkqxfJdhBzjq0M5tiMq0u0VVWks5uBl76gaJpZM4U5GAS

.

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/soscripted/sox/issues/338#issuecomment-401451992, or mute the thread https://github.com/notifications/unsubscribe-auth/AGBDSwXnv8txCu1MVCopm9MGBugYSP_0ks5uBoA3gaJpZM4U5GAS .

shu8 commented 6 years ago

@GaurangTandon fair enough, but I'm sure new users wouldn't mind a 'new in this version' popup anyway seeing as it is technically a new version for them πŸ˜†

I'll try doing something with this soon.

GaurangTandon commented 6 years ago

I just noticed that this has already been implemented in a much cooler manner by Tomas Zato on StackApps. Considering that it's 986 lines worth of code, I'm sure it can't be merged into SOX. So, it'd rather be separately on its own.

<Thought I'd just mention.>