diff --git a/js/modules/link_previews.js b/js/modules/link_previews.js index 3c3f7c5a7b..6e35613fbe 100644 --- a/js/modules/link_previews.js +++ b/js/modules/link_previews.js @@ -1,5 +1,6 @@ /* global URL */ +const { isNumber, compact } = require('lodash'); const he = require('he'); const LinkifyIt = require('linkify-it'); @@ -96,9 +97,28 @@ function getImageMetaTag(html) { return _getMetaTag(html, META_IMAGE); } -function findLinks(text) { +function findLinks(text, caretLocation) { + const haveCaretLocation = isNumber(caretLocation); + const textLength = text ? text.length : 0; + const matches = linkify.match(text || '') || []; - return matches.map(match => match.text); + return compact( + matches.map(match => { + if (!haveCaretLocation) { + return match.text; + } + + if (match.lastIndex === textLength && caretLocation === textLength) { + return match.text; + } + + if (match.index > caretLocation || match.lastIndex < caretLocation) { + return match.text; + } + + return null; + }) + ); } function getDomain(url) { diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index a0ba2c5fb5..477b8fa487 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -1700,6 +1700,7 @@ } const messageText = this.$messageField.val().trim(); + const caretLocation = this.$messageField.get(0).selectionStart; if (!messageText) { this.resetLinkPreview(); @@ -1709,7 +1710,10 @@ return; } - const links = window.Signal.LinkPreviews.findLinks(messageText); + const links = window.Signal.LinkPreviews.findLinks( + messageText, + caretLocation + ); const { currentlyMatchedLink } = this; if (links.includes(currentlyMatchedLink)) { return; diff --git a/test/modules/link_previews_test.js b/test/modules/link_previews_test.js index 327f98b6d0..8baec0e96a 100644 --- a/test/modules/link_previews_test.js +++ b/test/modules/link_previews_test.js @@ -1,6 +1,7 @@ const { assert } = require('chai'); const { + findLinks, getTitleMetaTag, getImageMetaTag, isLinkInWhitelist, @@ -228,4 +229,80 @@ describe('Link previews', () => { ); }); }); + + describe('#findLinks', () => { + it('returns all links if no caretLocation is provided', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + + const expected = [ + 'https://github.com/signalapp/Signal-Desktop', + 'https://github.com/signalapp/Signal-Android', + ]; + + const actual = findLinks(text); + assert.deepEqual(expected, actual); + }); + + it('includes all links if cursor is not in a link', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + const caretLocation = 10; + + const expected = [ + 'https://github.com/signalapp/Signal-Desktop', + 'https://github.com/signalapp/Signal-Android', + ]; + + const actual = findLinks(text, caretLocation); + assert.deepEqual(expected, actual); + }); + + it('excludes a link not at the end if the caret is inside of it', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + const caretLocation = 30; + + const expected = ['https://github.com/signalapp/Signal-Android']; + + const actual = findLinks(text, caretLocation); + assert.deepEqual(expected, actual); + }); + + it('excludes a link not at the end if the caret is at its end', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + const caretLocation = 64; + + const expected = ['https://github.com/signalapp/Signal-Android']; + + const actual = findLinks(text, caretLocation); + assert.deepEqual(expected, actual); + }); + + it('excludes a link at the end of the caret is inside of it', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + const caretLocation = 100; + + const expected = ['https://github.com/signalapp/Signal-Desktop']; + + const actual = findLinks(text, caretLocation); + assert.deepEqual(expected, actual); + }); + + it('includes link at the end if cursor is at its end', () => { + const text = + 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; + const caretLocation = text.length; + + const expected = [ + 'https://github.com/signalapp/Signal-Desktop', + 'https://github.com/signalapp/Signal-Android', + ]; + + const actual = findLinks(text, caretLocation); + assert.deepEqual(expected, actual); + }); + }); });