mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {AppBskyRichtextFacet, RichText} from '@atproto/api'
2
3export type LinkFacetMatch = {
4 rt: RichText
5 facet: AppBskyRichtextFacet.Main
6}
7
8export function suggestLinkCardUri(
9 mayBePaste: boolean,
10 nextDetectedUris: Map<string, LinkFacetMatch>,
11 prevDetectedUris: Map<string, LinkFacetMatch>,
12 pastSuggestedUris: Set<string>,
13): string | undefined {
14 const suggestedUris = new Set<string>()
15 for (const [uri, nextMatch] of nextDetectedUris) {
16 if (!isValidUrlAndDomain(uri)) {
17 continue
18 }
19 if (pastSuggestedUris.has(uri)) {
20 // Don't suggest already added or already dismissed link cards.
21 continue
22 }
23 if (mayBePaste) {
24 // Immediately add the pasted link without waiting to type more.
25 suggestedUris.add(uri)
26 continue
27 }
28 const prevMatch = prevDetectedUris.get(uri)
29 if (!prevMatch) {
30 // If the same exact link wasn't already detected during the last keystroke,
31 // it means you're probably still typing it. Disregard until it stabilizes.
32 continue
33 }
34 const prevTextAfterUri = prevMatch.rt.unicodeText.slice(
35 prevMatch.facet.index.byteEnd,
36 )
37 const nextTextAfterUri = nextMatch.rt.unicodeText.slice(
38 nextMatch.facet.index.byteEnd,
39 )
40 if (prevTextAfterUri === nextTextAfterUri) {
41 // The text you're editing is before the link, e.g.
42 // "abc google.com" -> "abcd google.com".
43 // This is a good time to add the link.
44 suggestedUris.add(uri)
45 continue
46 }
47 if (/^\s/m.test(nextTextAfterUri)) {
48 // The link is followed by a space, e.g.
49 // "google.com" -> "google.com " or
50 // "google.com." -> "google.com ".
51 // This is a clear indicator we can linkify it.
52 suggestedUris.add(uri)
53 continue
54 }
55 if (
56 /^[)]?[.,:;!?)](\s|$)/m.test(prevTextAfterUri) &&
57 /^[)]?[.,:;!?)]\s/m.test(nextTextAfterUri)
58 ) {
59 // The link was *already* being followed by punctuation,
60 // and now it's followed both by punctuation and a space.
61 // This means you're typing after punctuation, e.g.
62 // "google.com." -> "google.com. " or
63 // "google.com.foo" -> "google.com. foo".
64 // This means you're not typing the link anymore, so we can linkify it.
65 suggestedUris.add(uri)
66 continue
67 }
68 }
69 for (const uri of pastSuggestedUris) {
70 if (!nextDetectedUris.has(uri)) {
71 // If a link is no longer detected, it's eligible for suggestions next time.
72 pastSuggestedUris.delete(uri)
73 }
74 }
75
76 let suggestedUri: string | undefined
77 if (suggestedUris.size > 0) {
78 suggestedUri = Array.from(suggestedUris)[0]
79 pastSuggestedUris.add(suggestedUri)
80 }
81
82 return suggestedUri
83}
84
85// https://stackoverflow.com/questions/8667070/javascript-regular-expression-to-validate-url
86// question credit Muhammad Imran Tariq https://stackoverflow.com/users/420613/muhammad-imran-tariq
87// answer credit Christian David https://stackoverflow.com/users/967956/christian-david
88function isValidUrlAndDomain(value: string) {
89 return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
90 value,
91 )
92}