@@ -102,7 +102,6 @@ export function setupTwitterObserver(
102102 observer . observe ( twitterReactRoot , { childList : true , subtree : true } ) ;
103103 } ) ;
104104}
105-
106105async function handleNewNode (
107106 node : Element ,
108107 config : ActionAdapter ,
@@ -114,17 +113,25 @@ async function handleNewNode(
114113 if ( ! element || element . localName !== 'div' ) {
115114 return ;
116115 }
117- const rootElement = findElementByTestId ( element , 'card.wrapper' ) ;
118- if ( ! rootElement ) {
119- return ;
120- }
121- // handle link preview only, assuming that link preview is a must for actions
122- const linkPreview = rootElement . children [ 0 ] as HTMLDivElement ;
123- if ( ! linkPreview ) {
124- return ;
116+
117+ let anchor ;
118+ let card ;
119+ let tweetText ;
120+
121+ const linkPreview = tryLinkPreview ( element ) ;
122+ if ( linkPreview ) {
123+ anchor = linkPreview . anchor ;
124+ card = linkPreview . card ;
125+ } else {
126+ const link = tryLinkInText ( element ) ;
127+ if ( link ) {
128+ anchor = link . anchor ;
129+ tweetText = link . tweetText ;
130+ }
125131 }
126132
127- const anchor = linkPreview . children [ 0 ] as HTMLAnchorElement ;
133+ if ( ! anchor ) return ;
134+
128135 const shortenedUrl = anchor . href ;
129136 const actionUrl = await resolveTwitterShortenedUrl ( shortenedUrl ) ;
130137 const interstitialData = isInterstitial ( actionUrl ) ;
@@ -168,13 +175,22 @@ async function handleNewNode(
168175 return ;
169176 }
170177
178+ //double check if link preview appeared after we assumed it is not there
179+ const isCardPresent = findCardInTweet ( element ) ;
180+ if ( ! card && isCardPresent ) {
181+ console . log ( 'found card in tweet' ) ;
182+ return ;
183+ }
184+
171185 const action = await Action . fetch ( actionApiUrl , config ) . catch ( ( ) => null ) ;
172186
173187 if ( ! action ) {
174188 return ;
175189 }
176190
177- rootElement . parentElement ?. replaceChildren (
191+ const container = card ? card . parentElement : getContainerForLink ( tweetText ! ) ;
192+
193+ container ?. replaceChildren (
178194 createAction ( {
179195 originalUrl : actionUrl ,
180196 action,
@@ -242,3 +258,50 @@ function findElementByTestId(element: Element, testId: string) {
242258 }
243259 return element . querySelector ( `[data-testid="${ testId } "]` ) ;
244260}
261+
262+ function getContainerForLink ( tweetText : Element ) {
263+ const root = document . createElement ( 'div' ) ;
264+ root . style . paddingTop = '12px' ;
265+ const dm = tweetText . closest ( `[data-testid="messageEntry"]` ) ;
266+ if ( dm ) {
267+ tweetText . parentElement ?. parentElement ?. prepend ( root ) ;
268+ root . style . paddingBottom = '8px' ;
269+ } else {
270+ tweetText . parentElement ?. append ( root ) ;
271+ }
272+ return root ;
273+ }
274+
275+ function findCardInTweet ( element : Element ) {
276+ const message =
277+ findElementByTestId ( element , 'tweet' ) ??
278+ findElementByTestId ( element , 'messageEntry' ) ;
279+ if ( message ) {
280+ return findElementByTestId ( message , 'card.wrapper' ) ;
281+ }
282+ }
283+
284+ function tryLinkPreview ( element : Element ) {
285+ const card = findElementByTestId ( element , 'card.wrapper' ) ;
286+ if ( card ) {
287+ const linkPreview = card . children [ 0 ] ;
288+ if ( linkPreview ) {
289+ const anchor = linkPreview . children [ 0 ] as HTMLAnchorElement ;
290+ return { anchor, card } ;
291+ }
292+ }
293+ }
294+ function tryLinkInText ( element : Element ) {
295+ const tweetText = findElementByTestId ( element , 'tweetText' ) ;
296+ if ( ! tweetText || tweetText . classList . contains ( 'dialect-link-tweet' ) ) {
297+ return ;
298+ }
299+
300+ const links = tweetText . getElementsByTagName ( 'a' ) ;
301+ if ( links . length > 0 ) {
302+ //marking tweet as visited
303+ tweetText . classList . add ( 'dialect-link-tweet' ) ;
304+ const anchor = links [ links . length - 1 ] as HTMLAnchorElement ;
305+ return { anchor, tweetText } ;
306+ }
307+ }
0 commit comments