- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 676
 
messageActionSheet: Add option to copy link of message to clipboard #3865
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
base: main
Are you sure you want to change the base?
Conversation
f36cade    to
    43ad7da      
    Compare
  
    | 
           Adding a cross-reference: I think this is meant to fix #2623.  | 
    
          
 The essential thing in the PR branch is just that it adds to   | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @chrisbobbe for picking this up! Some small comments below.
One UI comment: I think we can remove the "Share" option. It seems like a lot to have three of these:
"""
Copy message content
Copy message link
Share
"""
and the action sheet is pretty overwhelming already.
        
          
                src/utils/internalLinks.js
              
                Outdated
          
        
      | 
               | 
          ||
| /** | ||
| * Produce the operand of a `stream` operator from a message. | ||
| * See encode_stream_id in `static/js/util` in the web app. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hash_util, I think
        
          
                src/utils/internalLinks.js
              
                Outdated
          
        
      | * */ | ||
| const unparseStreamOperand = (message: Message) => { | ||
| const name = (message.display_recipient || 'unknown').replace(' ', '-'); | ||
| return `${encodeHashComponent(message.stream_id.toString())}-${encodeHashComponent(name)}`; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be made slightly closer to the webapp code, and slightly shorter, by making one encodeHashComponent call after the ${...}-${...} step.
| * See encode_stream_id in `static/js/util` in the web app. | ||
| * */ | ||
| const unparseStreamOperand = (message: Message) => { | ||
| const name = (message.display_recipient || 'unknown').replace(' ', '-'); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My first reaction to this unknown bit was that that seems like a sign we should raise an error instead. But that's what the webapp does! In this case I think aligning with the webapp code wins, particularly as this is logic we'd ideally like to share in the future.
        
          
                src/utils/internalLinks.js
              
                Outdated
          
        
      | message.type === 'stream' | ||
| ? `stream/${unparseStreamOperand(message)}/topic/${unparseTopicOperand(message)}` | ||
| : `pm-with/${unparsePmOperand(message)}`; | ||
| return encodeURI( | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why encodeURI? It looks like that differs from the webapp -- and it seems like it'd cause a second round of %-encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... I guess that's probably a no-op, because of our dot-encoding? Cleaner to leave it off, though.
| }); | ||
| test('properly encodes streams with tricky characters', () => { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: blank lines between tests (here and below)
        
          
                src/message/messageActionSheet.js
              
                Outdated
          
        
      | Clipboard.setString(getLinkToMessage(auth.realm, message)); | ||
| showToast(_('Copied link to message')); | ||
| }; | ||
| copyMessageLink.title = 'Copy message link'; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at this and thought "gosh, that sounds ambiguous -- what about when the message contains a link? won't it sound like this means to copy that link?"
Then I found there'd been discussion of exactly that on the previous PR: #3412 (comment) 🙂 and I guess my conclusion was:
I think it's still a little ambiguous what "message link" means [...] Is it "Copy a link to the message", or "Copy the link in the message"? But it's good enough I'll be happy to merge it, and we can wordsmith further later.
So that seems reasonable -- I'll stand by that. If you have some ideas for that further wordsmithing, though, I'm interested!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that the success string just above is "Copied link to message", I'd say present-tensing that as "Copy link to message" is probably for the best.
| "Copy message content": "Copy message content", | ||
| "Copied message content": "Copied message content", | ||
| "Copy message link": "Copy message link", | ||
| "Copied link to message": "Copied link to message", | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a few more new/edited strings, like "Failed to copy message content to clipboard".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks
        
          
                src/message/messageActionSheet.js
              
                Outdated
          
        
      | const rawMessage = isAnOutboxMessage(message) /* $FlowFixMe: then really type Outbox */ | ||
| ? message.markdownContent | ||
| : (await api.getRawMessageContent(auth, message.id)).raw_content; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the last commit when getRawMessageContent gets introduced, this spot should switch to it. Basically that new function is the result of factoring out this statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few trivialities:
| subject: '.%%2E/#\ud83d\udc4b', | ||
| }); | ||
| expect(getLinkToMessage(realm, message)).toEqual( | ||
| `${realm}/#narrow/stream/123-general/topic/.2E.25.252E.2F.23.F0.9F.91.8B/near/${message.id}`, | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may also want to add a non-ASCII Unicode character in the BMP (i.e., one not represented by a pair of surrogates in UTF-16).
        
          
                src/message/messageActionSheet.js
              
                Outdated
          
        
      | Clipboard.setString(getLinkToMessage(auth.realm, message)); | ||
| showToast(_('Copied link to message')); | ||
| }; | ||
| copyMessageLink.title = 'Copy message link'; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that the success string just above is "Copied link to message", I'd say present-tensing that as "Copy link to message" is probably for the best.
        
          
                src/message/messageActionSheet.js
              
                Outdated
          
        
      | const isAnOutboxMessage = (message: Message | Outbox): boolean => message.isOutbox; | ||
| 
               | 
          ||
| const getRawMessageContent = async (auth: Auth, message: Message | Outbox) => | ||
| isAnOutboxMessage(message) /* $FlowFixMe: then really type Outbox */ | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be fixable simply by testing message.isOutbox inline, rather than calling isAnOutboxMessage.
          
 I tend to agree, but just for completeness, here's a consolidated list of issues related to the "Share" option (and thus that would be affected by its removal), and @jainkuniya's comment in support of keeping it: #3796 Let me know how you'd like to proceed.  | 
    
Add getLinkToMessage, on the model of by_conversation_and_time_uri in static/js/hash_util.js in zulip/zulip, to prepare for the child, which adds an option in the message action sheet to share a link to the message.
Use `getLinkToMessage`, introduced in the parent, to enable an option, called 'Copy message link', from the message action sheet to copy a link to the message to the clipboard. Preserve the option to copy the message's content, but rename that to 'copy message content.' Fixes: zulip#2623.
…essage. This may not be kept around forever because copying a link and copying a message's contents are both supported. But now there's a way to do both in the same action, from the Share menu.
Make `getRawMessageContent` as a proxy for getting the raw (markdown) message content, whether from an API request, in the case of a Message, or directly from the object, in case of an Outbox.
43ad7da    to
    52cfb9c      
    Compare
  
    | 
           I've made the other changes; just my question in #3865 (comment) remains, I think.  | 
    
| 
           Probably what we need is some sort of hierarchical menu: ... and while I'm sure there's an easy-ish way to do that in React Native, I'm not familiar with it.  | 
    
STATUS EDIT: This has gone stale; I'd be happy for a new PR to be made (particularly with #4146 in mind, and perhaps #3757 will be done by the time we pick this up again), drawing from the work done here if that's helpful.
Continuation of #3412, responding to the changes requested in #3412 (comment).
These commits do introduce user-facing strings; should I get set up with Transifex or is that something you do, @gnprice?