about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state/models/notifications-view.ts72
-rw-r--r--src/view/com/notifications/FeedItem.tsx28
-rw-r--r--src/view/com/post/Post.tsx14
3 files changed, 86 insertions, 28 deletions
diff --git a/src/state/models/notifications-view.ts b/src/state/models/notifications-view.ts
index 80e5c80c6..f3163822e 100644
--- a/src/state/models/notifications-view.ts
+++ b/src/state/models/notifications-view.ts
@@ -1,6 +1,7 @@
-import {makeAutoObservable} from 'mobx'
+import {makeAutoObservable, runInAction} from 'mobx'
 import * as ListNotifications from '../../third-party/api/src/client/types/app/bsky/notification/list'
 import {RootStoreModel} from './root-store'
+import {PostThreadViewModel} from './post-thread-view'
 import {Declaration} from './_common'
 import {hasProp} from '../lib/type-guards'
 import {APP_BSKY_GRAPH} from '../../third-party/api'
@@ -34,6 +35,9 @@ export class NotificationsViewItemModel implements GroupedNotification {
   indexedAt: string = ''
   additional?: NotificationsViewItemModel[]
 
+  // additional data
+  additionalPost?: PostThreadViewModel
+
   constructor(
     public rootStore: RootStoreModel,
     reactKey: string,
@@ -89,6 +93,13 @@ export class NotificationsViewItemModel implements GroupedNotification {
     return this.reason === 'assertion'
   }
 
+  get needsAdditionalData() {
+    if (this.isUpvote || this.isRepost || this.isTrend || this.isReply) {
+      return !this.additionalPost
+    }
+    return false
+  }
+
   get isInvite() {
     return (
       this.isAssertion && this.record.assertion === APP_BSKY_GRAPH.AssertMember
@@ -107,6 +118,27 @@ export class NotificationsViewItemModel implements GroupedNotification {
     }
     return ''
   }
+
+  async fetchAdditionalData() {
+    if (!this.needsAdditionalData) {
+      return
+    }
+    let postUri
+    if (this.isReply) {
+      postUri = this.uri
+    } else if (this.isUpvote || this.isRead || this.isTrend) {
+      postUri = this.subjectUri
+    }
+    if (postUri) {
+      this.additionalPost = new PostThreadViewModel(this.rootStore, {
+        uri: postUri,
+        depth: 0,
+      })
+      await this.additionalPost.setup().catch(e => {
+        console.error('Failed to load post needed by notification', e)
+      })
+    }
+  }
 }
 
 export class NotificationsViewModel {
@@ -246,7 +278,7 @@ export class NotificationsViewModel {
         limit: PAGE_SIZE,
       })
       const res = await this.rootStore.api.app.bsky.notification.list(params)
-      this._replaceAll(res)
+      await this._replaceAll(res)
       this._xIdle()
     } catch (e: any) {
       this._xIdle(`Failed to load notifications: ${e.toString()}`)
@@ -264,7 +296,7 @@ export class NotificationsViewModel {
         before: this.loadMoreCursor,
       })
       const res = await this.rootStore.api.app.bsky.notification.list(params)
-      this._appendAll(res)
+      await this._appendAll(res)
       this._xIdle()
     } catch (e: any) {
       this._xIdle(`Failed to load notifications: ${e.toString()}`)
@@ -296,25 +328,37 @@ export class NotificationsViewModel {
     }
   }
 
-  private _replaceAll(res: ListNotifications.Response) {
+  private async _replaceAll(res: ListNotifications.Response) {
     this.notifications.length = 0
-    this._appendAll(res)
+    return this._appendAll(res)
   }
 
-  private _appendAll(res: ListNotifications.Response) {
+  private async _appendAll(res: ListNotifications.Response) {
     this.loadMoreCursor = res.data.cursor
     this.hasMore = !!this.loadMoreCursor
     let counter = this.notifications.length
+    const promises = []
+    const itemModels: NotificationsViewItemModel[] = []
     for (const item of groupNotifications(res.data.notifications)) {
-      this._append(counter++, item)
+      const itemModel = new NotificationsViewItemModel(
+        this.rootStore,
+        `item-${counter++}`,
+        item,
+      )
+      if (itemModel.needsAdditionalData) {
+        promises.push(itemModel.fetchAdditionalData())
+      }
+      itemModels.push(itemModel)
     }
-  }
-
-  private _append(keyId: number, item: GroupedNotification) {
-    // TODO: validate .record
-    this.notifications.push(
-      new NotificationsViewItemModel(this.rootStore, `item-${keyId}`, item),
-    )
+    await Promise.all(promises).catch(e => {
+      console.error(
+        'Uncaught failure during notifications-view _appendAll()',
+        e,
+      )
+    })
+    runInAction(() => {
+      this.notifications = this.notifications.concat(itemModels)
+    })
   }
 
   private _updateAll(res: ListNotifications.Response) {
diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx
index 8741e4236..1b9fe51f0 100644
--- a/src/view/com/notifications/FeedItem.tsx
+++ b/src/view/com/notifications/FeedItem.tsx
@@ -4,11 +4,12 @@ import {StyleSheet, Text, View} from 'react-native'
 import {AtUri} from '../../../third-party/uri'
 import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome'
 import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
+import {PostThreadViewModel} from '../../../state/models/post-thread-view'
 import {s, colors} from '../../lib/styles'
 import {ago, pluralize} from '../../../lib/strings'
 import {UpIconSolid} from '../../lib/icons'
 import {UserAvatar} from '../util/UserAvatar'
-import {PostText} from '../post/PostText'
+import {ErrorMessage} from '../util/ErrorMessage'
 import {Post} from '../post/Post'
 import {Link} from '../util/Link'
 import {InviteAccepter} from './InviteAccepter'
@@ -51,7 +52,7 @@ export const FeedItem = observer(function FeedItem({
         ]}
         href={itemHref}
         title={itemTitle}>
-        <Post uri={item.uri} />
+        <Post uri={item.uri} initView={item.additionalPost} />
       </Link>
     )
   }
@@ -170,7 +171,7 @@ export const FeedItem = observer(function FeedItem({
             </Text>
           </View>
           {item.isUpvote || item.isRepost || item.isTrend ? (
-            <PostText uri={item.subjectUri} style={[s.gray5]} />
+            <AdditionalPostText additionalPost={item.additionalPost} />
           ) : (
             <></>
           )}
@@ -181,17 +182,24 @@ export const FeedItem = observer(function FeedItem({
           <InviteAccepter item={item} />
         </View>
       )}
-      {item.isReply ? (
-        <View style={s.pt5}>
-          <Post uri={item.uri} />
-        </View>
-      ) : (
-        <></>
-      )}
     </Link>
   )
 })
 
+function AdditionalPostText({
+  additionalPost,
+}: {
+  additionalPost?: PostThreadViewModel
+}) {
+  if (!additionalPost) {
+    return <View />
+  }
+  if (additionalPost.error) {
+    return <ErrorMessage message={additionalPost.error} />
+  }
+  return <Text style={[s.gray5]}>{additionalPost.thread?.record.text}</Text>
+}
+
 const styles = StyleSheet.create({
   outer: {
     backgroundColor: colors.white,
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 4d668cac3..033cc6560 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -15,19 +15,25 @@ import {UserAvatar} from '../util/UserAvatar'
 import {useStores} from '../../../state'
 import {s, colors} from '../../lib/styles'
 
-export const Post = observer(function Post({uri}: {uri: string}) {
+export const Post = observer(function Post({
+  uri,
+  initView,
+}: {
+  uri: string
+  initView?: PostThreadViewModel
+}) {
   const store = useStores()
-  const [view, setView] = useState<PostThreadViewModel | undefined>()
+  const [view, setView] = useState<PostThreadViewModel | undefined>(initView)
   const [deleted, setDeleted] = useState(false)
 
   useEffect(() => {
-    if (view?.params.uri === uri) {
+    if (initView || view?.params.uri === uri) {
       return // no change needed? or trigger refresh?
     }
     const newView = new PostThreadViewModel(store, {uri, depth: 0})
     setView(newView)
     newView.setup().catch(err => console.error('Failed to fetch post', err))
-  }, [uri, view?.params.uri, store])
+  }, [initView, uri, view?.params.uri, store])
 
   // deleted
   // =