From 90ec22a6749555f48bfd21eec81f877f8eae0524 Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Mon, 10 Jun 2024 11:44:13 -0700
Subject: [PATCH] Add support for new-tab clicks on feeds (#4462)

---
 src/components/Link.tsx               | 14 ++-------
 src/platform/urls.tsx                 | 15 +++++++++-
 src/view/com/feeds/FeedSourceCard.tsx | 41 ++++++++++++++++++++-------
 3 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index a2e952a6..d8ac829b 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -12,7 +12,8 @@ import {
   isExternalUrl,
   linkRequiresWarning,
 } from '#/lib/strings/url-helpers'
-import {isNative, isWeb} from '#/platform/detection'
+import {isNative} from '#/platform/detection'
+import {shouldClickOpenNewTab} from '#/platform/urls'
 import {useModalControls} from '#/state/modals'
 import {useOpenLink} from '#/state/preferences/in-app-browser'
 import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped'
@@ -116,16 +117,7 @@ export function useLink({
         if (isExternal) {
           openLink(href)
         } else {
-          /**
-           * A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
-           * of @ts-ignore below.
-           */
-          const event = e as any
-          const isMiddleClick = isWeb && event.button === 1
-          const isMetaKey =
-            isWeb &&
-            (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
-          const shouldOpenInNewTab = isMetaKey || isMiddleClick
+          const shouldOpenInNewTab = shouldClickOpenNewTab(e)
 
           if (isBskyDownloadUrl(href)) {
             shareUrl(BSKY_DOWNLOAD_URL)
diff --git a/src/platform/urls.tsx b/src/platform/urls.tsx
index fd844d93..fd9d297a 100644
--- a/src/platform/urls.tsx
+++ b/src/platform/urls.tsx
@@ -1,4 +1,5 @@
-import {Linking} from 'react-native'
+import {GestureResponderEvent, Linking} from 'react-native'
+
 import {isNative, isWeb} from './detection'
 
 export async function getInitialURL(): Promise<string | undefined> {
@@ -23,3 +24,15 @@ export function clearHash() {
     window.location.hash = ''
   }
 }
+
+export function shouldClickOpenNewTab(e: GestureResponderEvent) {
+  /**
+   * A `GestureResponderEvent`, but cast to `any` to avoid using a bunch
+   * of @ts-ignore below.
+   */
+  const event = e as any
+  const isMiddleClick = isWeb && event.button === 1
+  const isMetaKey =
+    isWeb && (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
+  return isMetaKey || isMiddleClick
+}
diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx
index 589f674b..a6178943 100644
--- a/src/view/com/feeds/FeedSourceCard.tsx
+++ b/src/view/com/feeds/FeedSourceCard.tsx
@@ -1,5 +1,12 @@
 import React from 'react'
-import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
+import {
+  Linking,
+  Pressable,
+  StyleProp,
+  StyleSheet,
+  View,
+  ViewStyle,
+} from 'react-native'
 import {AtUri} from '@atproto/api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {msg, Plural, Trans} from '@lingui/macro'
@@ -26,6 +33,7 @@ import {RichText} from '#/components/RichText'
 import {Text} from '../util/text/Text'
 import {UserAvatar} from '../util/UserAvatar'
 import hairlineWidth = StyleSheet.hairlineWidth
+import {shouldClickOpenNewTab} from '#/platform/urls'
 
 export function FeedSourceCard({
   feedUri,
@@ -203,17 +211,30 @@ export function FeedSourceCardLoaded({
           style,
           {borderTopWidth: hideTopBorder ? 0 : hairlineWidth},
         ]}
-        onPress={() => {
+        onPress={e => {
+          const shouldOpenInNewTab = shouldClickOpenNewTab(e)
           if (feed.type === 'feed') {
-            navigation.push('ProfileFeed', {
-              name: feed.creatorDid,
-              rkey: new AtUri(feed.uri).rkey,
-            })
+            if (shouldOpenInNewTab) {
+              Linking.openURL(
+                `/profile/${feed.creatorDid}/feed/${new AtUri(feed.uri).rkey}`,
+              )
+            } else {
+              navigation.push('ProfileFeed', {
+                name: feed.creatorDid,
+                rkey: new AtUri(feed.uri).rkey,
+              })
+            }
           } else if (feed.type === 'list') {
-            navigation.push('ProfileList', {
-              name: feed.creatorDid,
-              rkey: new AtUri(feed.uri).rkey,
-            })
+            if (shouldOpenInNewTab) {
+              Linking.openURL(
+                `/profile/${feed.creatorDid}/lists/${new AtUri(feed.uri).rkey}`,
+              )
+            } else {
+              navigation.push('ProfileList', {
+                name: feed.creatorDid,
+                rkey: new AtUri(feed.uri).rkey,
+              })
+            }
           }
         }}
         key={feed.uri}>