From 486ce26a91fbcda5697ceb2ce8e238037ddfa081 Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Fri, 11 Nov 2022 12:49:53 -0600
Subject: Handle pending invites correctly

---
 src/view/com/modals/InviteToScene.tsx | 92 +++++++++++++++++++++++++++++++----
 1 file changed, 83 insertions(+), 9 deletions(-)

(limited to 'src/view/com/modals/InviteToScene.tsx')

diff --git a/src/view/com/modals/InviteToScene.tsx b/src/view/com/modals/InviteToScene.tsx
index 065df6350..2d4e372c1 100644
--- a/src/view/com/modals/InviteToScene.tsx
+++ b/src/view/com/modals/InviteToScene.tsx
@@ -1,4 +1,5 @@
 import React, {useState, useEffect, useMemo} from 'react'
+import {observer} from 'mobx-react-lite'
 import Toast from '../util/Toast'
 import {
   ActivityIndicator,
@@ -23,13 +24,18 @@ import {ErrorMessage} from '../util/ErrorMessage'
 import {useStores} from '../../../state'
 import * as apilib from '../../../state/lib/api'
 import {ProfileViewModel} from '../../../state/models/profile-view'
-import {SuggestedInvites} from '../../../state/models/suggested-invites'
+import {SuggestedInvitesView} from '../../../state/models/suggested-invites-view'
+import {Assertion} from '../../../state/models/get-assertions-view'
 import {FollowItem} from '../../../state/models/user-follows-view'
 import {s, colors} from '../../lib/styles'
 
 export const snapPoints = ['70%']
 
-export function Component({profileView}: {profileView: ProfileViewModel}) {
+export const Component = observer(function Component({
+  profileView,
+}: {
+  profileView: ProfileViewModel
+}) {
   const store = useStores()
   const layout = useWindowDimensions()
   const [index, setIndex] = useState(0)
@@ -40,12 +46,18 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
   const [hasSetup, setHasSetup] = useState<boolean>(false)
   const [error, setError] = useState<string>('')
   const suggestions = useMemo(
-    () => new SuggestedInvites(store, {sceneDid: profileView.did}),
+    () => new SuggestedInvitesView(store, {sceneDid: profileView.did}),
     [profileView.did],
   )
   const [createdInvites, setCreatedInvites] = useState<Record<string, string>>(
     {},
   )
+  // TODO: it would be much better if we just used the suggestions view for the deleted pending invites
+  //       but mobx isnt picking up on the state change in suggestions.unconfirmed and I dont have
+  //       time to debug that right now -prf
+  const [deletedPendingInvites, setDeletedPendingInvites] = useState<
+    Record<string, boolean>
+  >({})
 
   useEffect(() => {
     let aborted = false
@@ -95,6 +107,28 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
     }
   }
 
+  const onPressDeleteInvite = async (assertion: Assertion) => {
+    setError('')
+    const urip = new AtUri(assertion.uri)
+    try {
+      await store.api.app.bsky.graph.assertion.delete({
+        did: profileView.did,
+        rkey: urip.rkey,
+      })
+      setDeletedPendingInvites({
+        [assertion.uri]: true,
+        ...deletedPendingInvites,
+      })
+      Toast.show('Invite removed', {
+        duration: Toast.durations.LONG,
+        position: Toast.positions.TOP,
+      })
+    } catch (e) {
+      setError('There was an issue with the invite. Please try again.')
+      console.error(e)
+    }
+  }
+
   const renderSuggestionItem = ({item}: {item: FollowItem}) => {
     const createdInvite = createdInvites[item.did]
     return (
@@ -124,6 +158,27 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
     )
   }
 
+  const renderPendingInviteItem = ({item}: {item: Assertion}) => {
+    const wasDeleted = deletedPendingInvites[item.uri]
+    if (wasDeleted) {
+      return <View />
+    }
+    return (
+      <ProfileCard
+        did={item.subject.did}
+        handle={item.subject.handle}
+        displayName={item.subject.displayName}
+        renderButton={() => (
+          <>
+            <FontAwesomeIcon icon="x" style={[s.mr5]} size={14} />
+            <Text style={[s.fw400, s.f14]}>Undo invite</Text>
+          </>
+        )}
+        onPressButton={() => onPressDeleteInvite(item)}
+      />
+    )
+  }
+
   const Suggestions = () => (
     <View style={s.flex1}>
       {hasSetup ? (
@@ -163,11 +218,30 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
   )
 
   const PendingInvites = () => (
-    <View>
-      <View style={styles.todoContainer}>
-        <Text style={styles.todoLabel}>
-          Pending invites are still being implemented. Check back soon!
-        </Text>
+    <View style={s.flex1}>
+      {suggestions.sceneAssertionsView.isLoading ? (
+        <ActivityIndicator />
+      ) : undefined}
+      <View style={s.flex1}>
+        {!suggestions.unconfirmed.length ? (
+          <Text
+            style={{
+              textAlign: 'center',
+              paddingTop: 10,
+              paddingHorizontal: 40,
+              fontWeight: 'bold',
+              color: colors.gray5,
+            }}>
+            No pending invites.
+          </Text>
+        ) : (
+          <FlatList
+            data={suggestions.unconfirmed}
+            keyExtractor={item => item._reactKey}
+            renderItem={renderPendingInviteItem}
+            style={s.flex1}
+          />
+        )}
       </View>
     </View>
   )
@@ -207,7 +281,7 @@ export function Component({profileView}: {profileView: ProfileViewModel}) {
       />
     </View>
   )
-}
+})
 
 const styles = StyleSheet.create({
   title: {
-- 
cgit 1.4.1

