stream-chat-react-native-core 5.9.1 → 5.10.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/ChannelList/ChannelList.js +4 -3
- package/lib/commonjs/components/ChannelList/ChannelList.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.js +13 -63
- package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.test.js +21 -15
- package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.test.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/utils/renderText.js +6 -6
- package/lib/commonjs/components/Message/MessageSimple/utils/renderText.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/ChannelList/ChannelList.js +4 -3
- package/lib/module/components/ChannelList/ChannelList.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/utils/parseLinks.js +13 -63
- package/lib/module/components/Message/MessageSimple/utils/parseLinks.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/utils/parseLinks.test.js +21 -15
- package/lib/module/components/Message/MessageSimple/utils/parseLinks.test.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/utils/renderText.js +6 -6
- package/lib/module/components/Message/MessageSimple/utils/renderText.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/ChannelList/ChannelList.d.ts +2 -0
- package/lib/typescript/components/Message/MessageSimple/utils/parseLinks.d.ts +3 -4
- package/package.json +3 -1
- package/src/components/ChannelList/ChannelList.tsx +6 -1
- package/src/components/Message/MessageSimple/utils/parseLinks.test.ts +52 -54
- package/src/components/Message/MessageSimple/utils/parseLinks.ts +11 -81
- package/src/components/Message/MessageSimple/utils/renderText.tsx +5 -5
- package/src/version.json +1 -1
|
@@ -2,55 +2,52 @@ import { parseLinksFromText } from './parseLinks';
|
|
|
2
2
|
|
|
3
3
|
describe('parseLinksFromText', () => {
|
|
4
4
|
it.each([
|
|
5
|
-
['www.getstream.io', 'www.getstream.io'],
|
|
6
|
-
['getstream.io', 'getstream.io'],
|
|
7
|
-
['scrn://team-chat',
|
|
8
|
-
['https://localhost', 'localhost'],
|
|
5
|
+
['https://www.getstream.io', 'https://www.getstream.io'],
|
|
6
|
+
['https://getstream.io', 'https://getstream.io'],
|
|
7
|
+
['scrn://team-chat', undefined],
|
|
8
|
+
['https://localhost', 'https://localhost'],
|
|
9
9
|
[
|
|
10
10
|
'https://localhost/with/path?and=query&multiple=params',
|
|
11
|
-
'localhost/with/path?and=query&multiple=params',
|
|
11
|
+
'https://localhost/with/path?and=query&multiple=params',
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
'https://localhost/with/path?and=query#fragment',
|
|
15
|
+
'https://localhost/with/path?and=query#fragment',
|
|
12
16
|
],
|
|
13
|
-
['
|
|
14
|
-
['reactnative.stream', 'reactnative.stream'],
|
|
17
|
+
['reactnative.stream', undefined],
|
|
15
18
|
[
|
|
16
19
|
'https://zh.wikipedia.org/wiki/挪威牛油危機',
|
|
17
|
-
'zh.wikipedia.org/wiki/%E6%8C%AA%E5%A8%81%E7%89%9B%E6%B2%B9%E5%8D%B1%E6%A9%9F',
|
|
20
|
+
'https://zh.wikipedia.org/wiki/%E6%8C%AA%E5%A8%81%E7%89%9B%E6%B2%B9%E5%8D%B1%E6%A9%9F',
|
|
18
21
|
],
|
|
19
22
|
[
|
|
20
23
|
'https://getstream.io/chat/docs/react-native/?language=javascript',
|
|
21
|
-
'getstream.io/chat/docs/react-native/?language=javascript',
|
|
24
|
+
'https://getstream.io/chat/docs/react-native/?language=javascript',
|
|
22
25
|
],
|
|
23
|
-
['127.0.0.1/local_(development)_server',
|
|
24
|
-
['https://a.co:8999/ab.php?p=12', 'a.co:8999/ab.php?p=12'],
|
|
26
|
+
['127.0.0.1/local_(development)_server', undefined],
|
|
27
|
+
['https://a.co:8999/ab.php?p=12', 'https://a.co:8999/ab.php?p=12'],
|
|
25
28
|
[
|
|
26
|
-
'
|
|
27
|
-
'help.apple.com/xcode/mac/current/#/devba7f53ad4',
|
|
29
|
+
'https://help.apple.com/xcode/mac/current/#/devba7f53ad4',
|
|
30
|
+
'https://help.apple.com/xcode/mac/current/#/devba7f53ad4',
|
|
28
31
|
],
|
|
29
32
|
])('Returns the encoded value of %p as %p', (link, expected) => {
|
|
30
33
|
const result = parseLinksFromText(link);
|
|
31
|
-
|
|
32
|
-
expect(result[0].encoded).toBe(expected);
|
|
34
|
+
expect(result[0]?.encodedUrl).toBe(expected);
|
|
33
35
|
});
|
|
34
|
-
|
|
35
36
|
it('parses fqdn', () => {
|
|
36
37
|
const input = `We have put the apim bol,
|
|
37
|
-
temporarily so :sj: we can later put the monitors on my grasp
|
|
38
|
-
on reality right now is
|
|
39
|
-
https://www.contrived-example.com:8080/sub/page.php?p1=1🇳🇴&p2=2#fragment-identifier
|
|
40
|
-
:)`;
|
|
41
|
-
|
|
38
|
+
temporarily so :sj: we can later put the monitors on my grasp
|
|
39
|
+
on reality right now is
|
|
40
|
+
https://www.contrived-example.com:8080/sub/page.php?p1=1🇳🇴&p2=2#fragment-identifier
|
|
41
|
+
:)`;
|
|
42
42
|
const result = parseLinksFromText(input);
|
|
43
|
-
|
|
44
43
|
expect(result).toEqual([
|
|
45
44
|
{
|
|
46
|
-
|
|
47
|
-
'www.contrived-example.com:8080/sub/page.php?p1=1%F0%9F%87%B3%F0%9F%87%B4&p2=2#fragment-identifier',
|
|
45
|
+
encodedUrl:
|
|
46
|
+
'https://www.contrived-example.com:8080/sub/page.php?p1=1%F0%9F%87%B3%F0%9F%87%B4&p2=2#fragment-identifier',
|
|
48
47
|
raw: 'https://www.contrived-example.com:8080/sub/page.php?p1=1🇳🇴&p2=2#fragment-identifier',
|
|
49
|
-
scheme: 'https://',
|
|
50
48
|
},
|
|
51
49
|
]);
|
|
52
50
|
});
|
|
53
|
-
|
|
54
51
|
it.each([
|
|
55
52
|
['support+rn@getstream.io'],
|
|
56
53
|
['support.rn@getstream.io'],
|
|
@@ -58,60 +55,61 @@ https://www.contrived-example.com:8080/sub/page.php?p1=1🇳🇴&p2=2#fragment-i
|
|
|
58
55
|
['support_rn@getstream.io'],
|
|
59
56
|
])('Can parse the email address %p', (email) => {
|
|
60
57
|
const result = parseLinksFromText(email);
|
|
61
|
-
|
|
62
|
-
expect(result[0].
|
|
63
|
-
expect(result[0].scheme).toBe('mailto:');
|
|
58
|
+
expect(result[0].encodedUrl).toBe('mailto:' + email);
|
|
59
|
+
expect(result[0].raw).toBe(email);
|
|
64
60
|
});
|
|
65
|
-
|
|
66
61
|
it("doesn't double the mailto prefix", () => {
|
|
67
62
|
const input = 'mailto:support@getstream.io';
|
|
68
|
-
|
|
69
63
|
const result = parseLinksFromText(input);
|
|
70
|
-
|
|
71
64
|
expect(result[0]).toEqual({
|
|
72
|
-
|
|
65
|
+
encodedUrl: input,
|
|
73
66
|
raw: input,
|
|
74
|
-
scheme: 'mailto:',
|
|
75
67
|
});
|
|
76
68
|
});
|
|
77
|
-
|
|
78
69
|
it('Does not falsely parse LINKs from text content', () => {
|
|
79
70
|
const input = `#This string exists to test that we don't produce false positives
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
[
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
getstream.io
|
|
88
|
-
`;
|
|
71
|
+
Existing links:
|
|
72
|
+
[already a parsed link](https://getstream.io/blog/react-native-how-to-build-bidirectional-infinite-scroll/)
|
|
73
|
+
[even a bogus one]( should not match )
|
|
74
|
+
## These should, however, be parsed:
|
|
75
|
+
www.getstream.io
|
|
76
|
+
getstream.io
|
|
77
|
+
`;
|
|
89
78
|
const result = parseLinksFromText(input);
|
|
90
|
-
|
|
79
|
+
console.log({ result });
|
|
91
80
|
expect(result).toHaveLength(2);
|
|
92
81
|
});
|
|
93
|
-
|
|
94
82
|
it('Encodes incomplete emoji unicode', () => {
|
|
95
83
|
const input = 'https://getstream.io/�';
|
|
96
84
|
const result = parseLinksFromText(input);
|
|
97
|
-
|
|
98
85
|
expect(result[0]).toEqual({
|
|
99
|
-
|
|
100
|
-
raw:
|
|
101
|
-
scheme: 'https://',
|
|
86
|
+
encodedUrl: 'https://getstream.io/%EF%BF%BD',
|
|
87
|
+
raw: input,
|
|
102
88
|
});
|
|
103
89
|
});
|
|
104
|
-
|
|
90
|
+
it('doest not report invalid tlds as urls', () => {
|
|
91
|
+
const input = `
|
|
92
|
+
%
|
|
93
|
+
% Not links
|
|
94
|
+
%
|
|
95
|
+
example.invalid
|
|
96
|
+
example.invalid/
|
|
97
|
+
http://.example.com
|
|
98
|
+
http://-example.com
|
|
99
|
+
hppt://example.com
|
|
100
|
+
example.coma
|
|
101
|
+
-example.coma
|
|
102
|
+
`;
|
|
103
|
+
const result = parseLinksFromText(input);
|
|
104
|
+
expect(result).toHaveLength(0);
|
|
105
|
+
});
|
|
105
106
|
it('does not parse a decimal number as a URL', () => {
|
|
106
107
|
const input = '123.456';
|
|
107
108
|
const result = parseLinksFromText(input);
|
|
108
|
-
|
|
109
109
|
expect(result).toHaveLength(0);
|
|
110
110
|
});
|
|
111
|
-
|
|
112
111
|
it.each([['@user'], ['@user.name']])('does not parse %p as a URL', (input) => {
|
|
113
112
|
const result = parseLinksFromText(input);
|
|
114
|
-
|
|
115
113
|
expect(result).toHaveLength(0);
|
|
116
114
|
});
|
|
117
115
|
});
|
|
@@ -1,40 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
* 📣 Note: Do not use named capture groups, as
|
|
3
|
-
* it is not yet available in Hermes.
|
|
4
|
-
*
|
|
5
|
-
* These helpers indend to make it easier to skim read
|
|
6
|
-
* the patterns below, but note that some are optional,
|
|
7
|
-
* specified in the patterns these are added to.
|
|
8
|
-
* */
|
|
9
|
-
const emailUserName = '[\\w+\\.~$_-]+';
|
|
10
|
-
const schema = `(\\w{2,15}:\\/\\/)`;
|
|
11
|
-
// something.tld OR 123.123.123.123
|
|
12
|
-
const domain = `((?:\\w+\\.[a-zA-Z]+)+(?:[^:\\/\\s]+)|(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))`;
|
|
13
|
-
const port = `(:[0-9]{1,5})`;
|
|
14
|
-
const path = `((?:\\/)?[^?\\s]+)`;
|
|
15
|
-
const queryString = `(\\?[^#\\s]+)`;
|
|
16
|
-
const fragment = `(#[\\w_-]+)`;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Match any email address, with and without `mailto:`
|
|
20
|
-
*/
|
|
21
|
-
const emailPattern = `(mailto:)?((?:${emailUserName})@(?:${domain}))`;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Match any string starting with something that seems like it's a schema.
|
|
25
|
-
* */
|
|
26
|
-
const schemePrefixedLinkPattern = `${schema}(\\S+)`;
|
|
1
|
+
import Linkify from 'linkify-it';
|
|
27
2
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* something.tld
|
|
31
|
-
*/
|
|
32
|
-
const fqdnLinkPattern = `${schema}?${domain}${port}?${path}?${queryString}?${fragment}?`;
|
|
33
|
-
|
|
34
|
-
interface Link {
|
|
35
|
-
encoded: string;
|
|
3
|
+
interface LinkInfo {
|
|
4
|
+
encodedUrl: string;
|
|
36
5
|
raw: string;
|
|
37
|
-
scheme: string;
|
|
38
6
|
}
|
|
39
7
|
|
|
40
8
|
/**
|
|
@@ -49,57 +17,19 @@ const removeMarkdownLinksFromText = (input: string) => input.replace(/\[[\w\s]+\
|
|
|
49
17
|
* */
|
|
50
18
|
const removeUserNamesFromText = (input: string) => input.replace(/^@\w+\.?\w/, '');
|
|
51
19
|
|
|
52
|
-
export const parseLinksFromText = (input: string):
|
|
53
|
-
let matches;
|
|
54
|
-
|
|
20
|
+
export const parseLinksFromText = (input: string): LinkInfo[] => {
|
|
55
21
|
const strippedInput = [removeMarkdownLinksFromText, removeUserNamesFromText].reduce(
|
|
56
22
|
(acc, fn) => fn(acc),
|
|
57
23
|
input,
|
|
58
24
|
);
|
|
59
25
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const emailRegExp = new RegExp(emailPattern, 'gi');
|
|
63
|
-
while ((matches = emailRegExp.exec(input)) !== null) {
|
|
64
|
-
const [raw, scheme = 'mailto:', displayValue] = matches;
|
|
65
|
-
results.push({ encoded: encodeURI(displayValue), raw, scheme });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* The two link patterns are checked with an "or" (`|`)
|
|
70
|
-
* to avoid overlapping matches being duplicated in the output.
|
|
71
|
-
* */
|
|
72
|
-
const linkRegex = new RegExp(`${fqdnLinkPattern}|${schemePrefixedLinkPattern}`, 'gi');
|
|
73
|
-
while ((matches = linkRegex.exec(strippedInput)) !== null) {
|
|
74
|
-
const [
|
|
75
|
-
raw,
|
|
76
|
-
fqdnScheme = '',
|
|
77
|
-
fqdnDomainName = '',
|
|
78
|
-
fqdnPort = '',
|
|
79
|
-
fqdnPath = '',
|
|
80
|
-
fqdnQueryStraing = '',
|
|
81
|
-
fqdnFragment = '',
|
|
82
|
-
schemePrefixedScheme = '',
|
|
83
|
-
schemePrefixedPath = '',
|
|
84
|
-
] = matches;
|
|
85
|
-
|
|
86
|
-
if (schemePrefixedScheme !== '') {
|
|
87
|
-
results.push({
|
|
88
|
-
encoded: encodeURI(schemePrefixedPath),
|
|
89
|
-
raw,
|
|
90
|
-
scheme: schemePrefixedScheme,
|
|
91
|
-
});
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
26
|
+
const linkify = Linkify();
|
|
27
|
+
const matches = linkify.match(strippedInput) ?? [];
|
|
94
28
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
raw,
|
|
100
|
-
scheme: fqdnScheme,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
29
|
+
const result: LinkInfo[] = matches.map((match) => {
|
|
30
|
+
const { raw, url } = match;
|
|
31
|
+
return { encodedUrl: encodeURI(url), raw };
|
|
32
|
+
});
|
|
103
33
|
|
|
104
|
-
return
|
|
34
|
+
return result;
|
|
105
35
|
};
|
|
@@ -109,15 +109,15 @@ export const renderText = <
|
|
|
109
109
|
if (!text) return null;
|
|
110
110
|
|
|
111
111
|
let newText = text.trim();
|
|
112
|
-
const
|
|
112
|
+
const linkInfos = parseLinksFromText(newText);
|
|
113
113
|
|
|
114
|
-
for (const
|
|
115
|
-
const displayLink = truncate(
|
|
114
|
+
for (const linkInfo of linkInfos) {
|
|
115
|
+
const displayLink = truncate(linkInfo.raw, {
|
|
116
116
|
length: 200,
|
|
117
117
|
omission: '...',
|
|
118
118
|
});
|
|
119
|
-
const markdown = `[${displayLink}](${
|
|
120
|
-
newText = newText.replace(
|
|
119
|
+
const markdown = `[${displayLink}](${linkInfo.encodedUrl})`;
|
|
120
|
+
newText = newText.replace(linkInfo.raw, markdown);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
newText = newText.replace(/[<&"'>]/g, '\\$&');
|
package/src/version.json
CHANGED