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

Add the ability to copy messages in Expensify.cash #1778

Closed
roryabraham opened this issue Mar 15, 2021 · 12 comments · Fixed by #2088
Closed

Add the ability to copy messages in Expensify.cash #1778

roryabraham opened this issue Mar 15, 2021 · 12 comments · Fixed by #2088
Assignees

Comments

@roryabraham
Copy link
Contributor

roryabraham commented Mar 15, 2021

If you haven’t already, check out our contributing guidelines for onboarding!


Internal issue: https://github.com/Expensify/Expensify/issues/147479
Internal Upwork Posting: https://www.upwork.com/ab/applicants/1371557081012895744/job-details
Public Upwork Posting: https://www.upwork.com/jobs/~013badfb341950b5a6


Expected Result:

The "Copy to Clipboard" button in the ReportActionContextMenu should copy the text context of a report action / message to the clipboard.

Actual Result:

The "Copy to Clipboard" button in the ReportActionContextMenu currently doesn't do anything:

image


image


image

Action Performed:

  1. Temporarily edit this function to always return true so that the ReportActionContextMenu will show (see screenshots above).
  2. Hover, right-click, or longPress a message in a report (called a reportAction), then click Copy to Clipboard

Workaround:

None.

Platform:

All platforms ->

Web
iOS
Android
Desktop App
Mobile Web

Notes/Photos/Videos:

  • A good clipboard library for React Native is @react-native-community/clipboard, so your solution should probably leverage that.
  • Your solution must work on all platforms.
  • A solution should copy only the plain-text version of a message, not HTML the html.
  • BONUS: Make it also work to copy images 😉
@MazenRefaat
Copy link

I'm trying to run it locally, don't know how to get API to run

@SameeraMadushan
Copy link
Contributor

SameeraMadushan commented Mar 16, 2021

Hi,
This is my proposal for this task.

Solution

To achieve copy to clipboard functionality, I will use the following techniques.

Platform wise the functionality is different. Therefore, I will create a utility function under libs/copyToClipboard with specifying platforms.

  • index.native.js for iOS and Android
  • index.js for Web, desktop, and mobile web

Web, Desktop and mobile web

  • Selection copy - Will use window.getSelection to get the selected text and store it to clipboard using navigator.clipboard.writeText
  • Message block - Will use navigator.clipboard.writeText to copy complete message block and text will be taken from the this.props.action.message[0].text
  • If the requirement is to copy the exact input user is given (mark down or text), I will use html-to-text npm package, then convert this.props.action.message[0].html to text and store it in clipboard. (eg: when the user copies a link, markdown will save in the clipboard.)

iOS and Android

  • Selection copy - User can't select a section of the message in a native environment. (It will show native copy)
  • Message block - Will use @react-native-clipboard/clipboard package to copy message as a string and text will be taken from the this.props.action.message[0].text
  • If the requirement is to copy the exact input user is given (mark down or text), I will use html-to-text npm package, then convert this.props.action.message[0].html to text and store it in clipboard. (eg: when the user copies a link, markdown will save in the clipboard.)

Copying Images

This will be a tricky part.
Currently, the image action will receive an image HTML tag instead of a URL. This can be done on the web, desktop and mobile web.
Fetch will be used to generate the blob of the image.

html: "<img src="https://www.expensify.com/chat-attachments/724107650/w_53e8c5e8a3bbd154ae29e0d1ac24cace4df1fa85.png.1024.jpg" data-expensify-source="https://www.expensify.com/chat-attachments/724107650/w_53e8c5e8a3bbd154ae29e0d1ac24cace4df1fa85.png" data-name="sign.png" />"
text: "[Attachment]"
type: "COMMENT"

In the native environment, will have to create a native bridge to enable this feature.

Thanks.

@parasharrajat
Copy link
Member

parasharrajat commented Mar 16, 2021

Hi, I would like to work on this issue. It has scope for me to learn the Native Modules.

Proposal

  1. Add an onPress property to the CONTEXT_ACTIONS in the ReportActionContextMenu.js which will be called on the OnPress action something like.
 {
        text: 'Copy to Clipboard',
        icon: Clipboard,
        onPress: (action) => {
            console.debug(action);
          if(image message) {
          // To be implemented with instructions in the next steps.
              ClipboardAPI.setImage(action.message[0].html);
         } else{
            ClipboardAPI.setString(action.message[0].text);
        },
    },
    ...............
 <ReportActionContextMenuItem
                    icon={contextAction.icon}
                    text={contextAction.text}
                    isMini={props.isMini}
                    onPress={() => contextAction.onPress(props.action)}
                    key={contextAction.text}
                />
  1. We will send the action prop from the Parent Context.
  2. Now as mentioned on the issue, we can utilize the import { Clipboard } from 'react-native; as we are using React Native v0.63.x which has his API. or we can use the @react-native-community/clipboard package which has the same API.
  3. If we are using the @react-native-community/clipboard lib we have to create a wrapper function to resolve the Clipboard API as
    @react-native-community/clipboard exports the Clipboard as default but react-native-web export it as named export.
  4. Above solution will provide copying text strings for All the Platforms..

Images

Copying images is not supported by either react-native or the package.

For Browser-based platforms

  1. React native web
    https://github.com/necolas/react-native-web/blob/66d01734ce3ffcfea61e05aa1b45015658b3f2af/packages/react-native-web/src/exports/Clipboard/index.js#L33-L46
    As you see that they are using a span and setting the textcontent which escapes the Html which is needed to copy the images on the Browsers.

So we can extend the lib and create a Method same as the react-native-web setString called setImage which uses a contenteditable div and we set the innerHTML with the HTML format of the action for image messages and then the copy will include the following.

type: text/html
            <img src="https://res.cloudinary.com/practicaldev/image/fetch/s---V8aGC_g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zzfake78sf0bqwswcfn4.png" alt="Copy Image to Clipboard via Javascript.">

For Native Platform IOS

  1. We can use the native Bridging as IOS has https://developer.apple.com/documentation/uikit/uipasteboard.
    Proof of concept here => https://stackoverflow.com/a/44423850/7462668.

For Native Platform Android

  1. This one is kind of tricky. We need native bridging.
    https://developer.android.com/guide/topics/text/copy-paste.html#Copying This article suggests using the URI for the complex data types.
    I am naive to Native Modules but I will give my best.

So In short

Create Clipboard lib which exports a class Clipboard that extends the Clipboard from the @react-native-community/clipboard or react-native and adds setImage method which will be implemented for native and browser-based platforms as per above instructions.

Questions:

  1. Do we need a selection copy or just the whole message as per the ContextMenu I think full message?
  2. So I see that Text of report Action has HTML tags for eg:
 Check out [this link](<a href="https://www.expensify.com/_devportal/tools/logSearch/#query=request_id" target="_blank" rel="noreferrer noopener">https://www.expensify.com/_devportal/tools/logSearch/#query=request_id</a>:(%22Ufjjim%22)+AND+timestamp:[2021-01-08T03:48:10.389Z+TO+2021-01-08T05:48:10.389Z]&amp;index=logs_expensify-008878)!

Do we need to remove the HTML tags from it like we have done for the last message preview in the sidebar?

@roryabraham
Copy link
Contributor Author

roryabraham commented Mar 22, 2021

@parasharrajat Your proposal looks pretty good, but I do have some comments:

  1. I think we'll want to use @react-native-community/clipboard component because the one that ships with RN is deprecated:

image

  1. We will send the action prop from the Parent Context.

Let's actually just replace the reportActionID prop in ReportActionContextMenu with reportAction, and pass the whole action instead of just the ID.

  1. Do we need a selection copy or just the whole message as per the ContextMenu I think full message?

Yep, this feature should just copy the whole message. On web/desktop, highlighting text and using CMD+C should still work, but on native platforms there won't really be a way to copy a partial message, because the longPress action that would typically allow one to highlight and select text will instead open the custom context menu.

  1. Do we need to remove the HTML tags from it like we have done for the last message preview in the sidebar

Yes, content copied to clipboard should have all HTML tags stripped first.


Now, the most important caveat of this proposal's acceptance is that I think for now we should just focus on getting text copied successfully. We can worry about images in a separate issue. We really don't want to manage or maintain any native code in Expensify.cash if we can avoid it, so the only really acceptable solution there in my opinion would to be to:

  1. Fork @react-native-community/clipboard, and implement image-copying in the fork.
  2. Point Expensify.cash at the fork and implement the new image copying functionality in Expensify.cash
  3. Submit a PR for this issue, get it merged.
  4. Once that's merge, update Expensify.cash to point at the correctly updated version of react-native-clipboard instead of the fork.

AFAIK this isn't really something we've done yet.

cc @marcaaron @tgolen @AndrewGable @Julesssss in case you guys have thoughts.

@roryabraham
Copy link
Contributor Author

Also @parasharrajat, after the text is copied to the clipboard, can we change the clipboard icon to a green checkmark and have the tooltip text display Copied! until the menu is closed and reopened. For a demo of a similar experience, try copying the branch name of the head branch of a pull request in Github:

image

@parasharrajat
Copy link
Member

parasharrajat commented Mar 22, 2021

@roryabraham Thanks for clarifying the doubts.
I also agree that we should move the images part to the new issue as this involves a little more and especially the native part. So
I will work over the text part for now.

About the tick, surely we can do that. But where do you want to show that? On the context menu or hover menu. I think it will involve making the tooltip controlled.

@tgolen
Copy link
Contributor

tgolen commented Mar 22, 2021

@roryabraham I like your proposal for the image support. I think we should contact the contributors directly and offer to pay them to do that issue, rather than just waiting for them to prioritize it on their own.

@roryabraham
Copy link
Contributor Author

Well, just to clarify the "context menu" and "hover menu" are just different manifestations of the ReportActionContextMenu, and they should never show at the same time. A similar effect with the checkmark should be implemented on both, ideally.

@mallenexpensify
Copy link
Contributor

@roryabraham Assigned to you since you'll be the one reviewing proposals

@roryabraham
Copy link
Contributor Author

roryabraham commented Mar 25, 2021

I've already chosen a contributor, and he has a WIP PR up 🚀

@roryabraham
Copy link
Contributor Author

Sorry for the confusion, but per our documented process:

The engineer assigned to the Expensify/Expensify Cash issue should review incoming proposals and give the go ahead to a contributor to start working on a PR and should assign the Expensify/Expensify Cash issue to the contributor.

So I'm actually re-assigning this to @parasharrajat

@kaushiktd
Copy link
Contributor

Hi,

This is Kaushik here and I would like to suggest a simple and effective explanation for this,

  1. Add an onPress property to call clipboard function
    ClipboardAPI.setString(action.message[0].text);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants