From b06cd36b3fb6f715470500c61018f00913953fd1 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Fri, 17 Feb 2023 17:37:31 +0800
Subject: [PATCH] Initial work to allow cross-instance linking

- Load current-instance relationship on remote-instance account sheet
- Add button to switch to current-instance status page from remote-instance status page
---
 src/components/account.jsx | 58 +++++++++++++++++++++++++--------
 src/pages/status.css       | 16 +++++++++
 src/pages/status.jsx       | 66 +++++++++++++++++++++-----------------
 3 files changed, 97 insertions(+), 43 deletions(-)

diff --git a/src/components/account.jsx b/src/components/account.jsx
index a1ce0148..e24d0958 100644
--- a/src/components/account.jsx
+++ b/src/components/account.jsx
@@ -17,6 +17,12 @@ import Link from './link';
 
 function Account({ account, instance: propInstance, onClose }) {
   const { masto, instance, authenticated } = api({ instance: propInstance });
+  const {
+    masto: currentMasto,
+    instance: currentInstance,
+    authenticated: currentAuthenticated,
+  } = api();
+  const sameInstance = instance === currentInstance;
   const [uiState, setUIState] = useState('default');
   const isString = typeof account === 'string';
   const [info, setInfo] = useState(isString ? null : account);
@@ -86,19 +92,44 @@ function Account({ account, instance: propInstance, onClose }) {
   const [relationship, setRelationship] = useState(null);
   const [familiarFollowers, setFamiliarFollowers] = useState([]);
   useEffect(() => {
-    if (info && authenticated) {
+    if (info) {
       const currentAccount = store.session.get('currentAccount');
-      if (currentAccount === id) {
-        // It's myself!
-        return;
-      }
-      setRelationshipUIState('loading');
-      setFamiliarFollowers([]);
-
+      let accountID;
       (async () => {
-        const fetchRelationships = masto.v1.accounts.fetchRelationships([id]);
+        if (sameInstance && authenticated) {
+          accountID = id;
+        } else if (!sameInstance && currentAuthenticated) {
+          // Grab this account from my logged-in instance
+          const acctHasInstance = info.acct.includes('@');
+          try {
+            const results = await currentMasto.v2.search({
+              q: acctHasInstance ? info.acct : `${info.username}@${instance}`,
+              type: 'accounts',
+              limit: 1,
+              resolve: true,
+            });
+            console.log('🥏 Fetched account from logged-in instance', results);
+            accountID = results.accounts[0].id;
+          } catch (e) {
+            console.error(e);
+          }
+        }
+
+        if (!accountID) return;
+
+        if (currentAccount === accountID) {
+          // It's myself!
+          return;
+        }
+
+        setRelationshipUIState('loading');
+        setFamiliarFollowers([]);
+
+        const fetchRelationships = currentMasto.v1.accounts.fetchRelationships([
+          accountID,
+        ]);
         const fetchFamiliarFollowers =
-          masto.v1.accounts.fetchFamiliarFollowers(id);
+          currentMasto.v1.accounts.fetchFamiliarFollowers(accountID);
 
         try {
           const relationships = await fetchRelationships;
@@ -316,12 +347,11 @@ function Account({ account, instance: propInstance, onClose }) {
                             );
                             if (yes) {
                               newRelationship =
-                                await masto.v1.accounts.unfollow(id);
+                                await currentMasto.v1.accounts.unfollow(id);
                             }
                           } else {
-                            newRelationship = await masto.v1.accounts.follow(
-                              id,
-                            );
+                            newRelationship =
+                              await currentMasto.v1.accounts.follow(id);
                           }
                           if (newRelationship) setRelationship(newRelationship);
                           setRelationshipUIState('default');
diff --git a/src/pages/status.css b/src/pages/status.css
index d40ed123..ca25dc38 100644
--- a/src/pages/status.css
+++ b/src/pages/status.css
@@ -35,3 +35,19 @@
   opacity: 0;
   pointer-events: none;
 }
+
+.status-deck footer {
+  position: sticky;
+  bottom: 0;
+  bottom: env(safe-area-inset-bottom);
+  font-size: 90%;
+  background-color: var(--bg-faded-blur-color);
+  backdrop-filter: blur(16px);
+  padding: 16px;
+  white-space: pre-wrap;
+  line-height: 1.2;
+}
+.status-deck footer > p:first-of-type {
+  margin-top: 0;
+  padding-top: 0;
+}
diff --git a/src/pages/status.jsx b/src/pages/status.jsx
index 9086aaac..12633056 100644
--- a/src/pages/status.jsx
+++ b/src/pages/status.jsx
@@ -40,7 +40,9 @@ function resetScrollPosition(id) {
 
 function StatusPage() {
   const { id, ...params } = useParams();
-  const { masto, instance, authenticated } = api({ instance: params.instance });
+  const { masto, instance } = api({ instance: params.instance });
+  const { masto: currentMasto, instance: currentInstance } = api();
+  const sameInstance = instance === currentInstance;
   const navigate = useNavigate();
   const snapStates = useSnapshot(states);
   const [statuses, setStatuses] = useState([]);
@@ -571,34 +573,6 @@ function StatusPage() {
                     <Icon icon="eye-open" />{' '}
                     <span>Show all sensitive content</span>
                   </MenuItem>
-                  {import.meta.env.DEV && !authenticated && (
-                    <MenuItem
-                      onClick={() => {
-                        (async () => {
-                          try {
-                            const { masto } = api();
-                            const results = await masto.v2.search({
-                              q: heroStatus.url,
-                              type: 'statuses',
-                              resolve: true,
-                              limit: 1,
-                            });
-                            if (results.statuses.length) {
-                              const status = results.statuses[0];
-                              navigate(`/s/${status.id}`);
-                            } else {
-                              throw new Error('No results');
-                            }
-                          } catch (e) {
-                            alert('Error: ' + e);
-                            console.error(e);
-                          }
-                        })();
-                      }}
-                    >
-                      See post in currently logged-in instance
-                    </MenuItem>
-                  )}
                 </Menu>
               )}
               <Link
@@ -756,6 +730,40 @@ function StatusPage() {
             )}
           </>
         )}
+        {!sameInstance && (
+          <footer class="">
+            <p>
+              This post is from another instance (<b>{instance}</b>), different
+              from your current logged-in instance (<b>{currentInstance}</b>).
+            </p>
+            <button
+              type="button"
+              onClick={() => {
+                (async () => {
+                  try {
+                    const results = await currentMasto.v2.search({
+                      q: heroStatus.url,
+                      type: 'statuses',
+                      resolve: true,
+                      limit: 1,
+                    });
+                    if (results.statuses.length) {
+                      const status = results.statuses[0];
+                      navigate(`/s/${status.id}`);
+                    } else {
+                      throw new Error('No results');
+                    }
+                  } catch (e) {
+                    alert('Error: ' + e);
+                    console.error(e);
+                  }
+                })();
+              }}
+            >
+              <Icon icon="transfer" /> Switch to my instance
+            </button>
+          </footer>
+        )}
       </div>
     </div>
   );